https://www.shalerb.org/ Shale GitHub RubyGems CONTENTS Shale Introduction Prerequisites Features Installation Convert data to Ruby Convert Ruby to data Custom mappings XML namespaces Using methods Pretty printing Generating Schema Compiling Schema Supported types Adapters # Shale Shale is a Ruby object mapper and serializer for JSON, YAML and XML. It allows you to parse JSON, YAML and XML data and convert it into Ruby data structures, as well as serialize data structures into JSON, YAML or XML. # Introduction Working with data serialization formats directly can be painfull. This is especially true for XML. Let's consider this simple example of adding an address to a person using Nokogiri: require 'nokogiri' doc = Nokogiri::XML(<<~XML) <person> XML address = Nokogiri::XML::Node.new('address', doc) street = Nokogiri::XML::Node.new('street', doc) street.content = 'Oxford Street' address.add_child(street) city = Nokogiri::XML::Node.new ('city', doc) city.content = 'London' address.add_child(city) doc.root.add_child(address) puts doc.to_xml That's a lot of code for very simple use case. Anything more complex and code complexity increases exponentially leading to a maintanace problems and errors. With Shale you can use Ruby objects to work with data converting it to/from JSON, YAML or XML. Let's convert the same example to Shale: require 'shale' class Address < Shale::Mapper attribute :street, Shale::Type::String attribute :city, Shale::Type::String end class Person < Shale::Mapper attribute :address, Address end person = Person.from_xml('<person>') person.address = Address.new (street: 'Oxford Street', city: 'London') puts person.to_xml That's much simpler and it stays simple when the code complexity increases. # Prerequisites * Ruby 2.7+ * Nokogiri (Optional) * Ox (Optional) Shale doesn't have external dependencies. It uses standard library's JSON, YAML and REXML parsers by default. If you need more performant solutions you can use custom libraries. Out of the box, Shale provides adapters for Nokogiri and Ox - see how to use them. # Features * Convert JSON, YAML and XML to Ruby data model * Convert Ruby data model to JSON, YAML and XML * Generate JSON and XML Schema from Ruby models * Compile JSON Schema into Ruby models * Out of the box support for JSON, YAML, Nokogiri, REXML and Ox parsers * Support for custom adapters # Installation Add this line to your application's Gemfile: gem 'shale' And then execute: $ bundle install Or install it yourself as: $ gem install shale # Convert data to Ruby Converting data to Ruby is as simple as defining model classes and calling from_<format> method on this class. e.g. Person.from_json (json_doc) Example: JSON to Ruby YAML to Ruby XML to Ruby Ruby Hash to Ruby Run Example # Convert Ruby to data To convert Ruby to data just define model class, initialize object and call to_<format> method on it. e.g. Person.new(name: 'John Doe').to_json Example: Ruby to JSON Ruby to YAML Ruby to XML Ruby to Hash Run Example # Custom mappings When you define a class and add attributes, underneath Shale creates an implicit mapping of keys (for JSON/YAML) and elements (for XML) to attributes. That is nice for setting up your data model quickly, but usually your data format doesn't match your data model so cleanly. That's why you can explicitly map keys, element, attributes from your data format to attributes on you Ruby model. Example: Ruby to JSON Ruby to YAML Ruby to Hash Run Example XML is more complicated format. * To map XML element use map_element * To map XML attribute use map_attribute * To map XML text node use map_content (it will map first text node of an element) * To change the name of the root element use root Run Example # Using XML namespaces To map namespaced elements and attributes use namespace and prefix properties on map_element and map_attribute To define default namespace for all elements use namespace declaration (this will define namespace only on elements, if you want to define namespace on an attribute, explicitly declare it on map_attribute). Example: Explicit namespace Default namespace Run Example # Using methods to extract and generate data If you need full controll over extracting and generating data you can use methods to do so. Example: JSON YAML XML Run Example # Pretty printing and XML declaration By default generated JSON and XML are compacted. If you need human readable format use :pretty parameter on #to_json and #to_xml person.to_json(:pretty) # => # # { # "name": "John Doe", # "address": { # "city": "London" # } # } You can also add an XML declaration by passing :declaration to # to_xml person.to_xml(:pretty, :declaration) # => # # # #
# # Generating JSON and XML Schema WARNING Shale only supports Draft 2020-12 JSON Schema To generate JSON or XML Schema from you Shale data model use: Example: JSON XML Run Example You can also use a command line tool to do it: $ shaleb -i data_model.rb -r Person -p -f json or XML Schema: $ shaleb -i data_model.rb -r Person -p -f xml If you want to convert your own types to Schema types use: Example: JSON XML Run Example # Compiling JSON Schema To compile JSON Schema and generate Ruby data model use: Example: JSON Run Example You can also use a command line tool to do it: $ shaleb -c -i schema.json -r Person # Supported types Shale supports these types out of the box: * Shale::Type::String * Shale::Type::Integer * Shale::Type::Float * Shale::Type::Boolean * Shale::Type::Date * Shale::Type::Time To add your own type extend defina a class and extend it from Shale::Type::Value and implement .cast class method. require 'shale/type/value' class MyIntegerType < Shale::Type::Value def self.cast(value) value.to_i end end # Adapters Shale uses adapters for parsing and generating documents. By default Ruby's standard JSON parser is used for handling JSON documents, YAML for YAML and REXML for XML. You can change it by providing your own adapter. For JSON and YAML, adapter must implement .load and .dump class methods. require 'shale' require 'multi_json' Shale.json_adapter = MultiJson Shale.yaml_adapter = MyYamlAdapter For XML, Shale provides adapters for most popular Ruby XML parsers: WARNING Ox parser doesn't support XML namespaces require 'shale' # REXML is used by default: require 'shale/adapter/ rexml' Shale.xml_adapter = Shale::Adapter::REXML # if you want to use Nokogiri: require 'shale/adapter/nokogiri' Shale.xml_adapter = Shale::Adapter::Nokogiri # or if you want to use Ox: require 'shale/ adapter/ox' Shale.xml_adapter = Shale::Adapter::Ox