• Stars
    star
    228
  • Rank 168,935 (Top 4 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 13 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Translates Ruby Hashes to XML

Gyoku

Gyoku translates Ruby Hashes to XML.

Gyoku.xml(:find_user => { :id => 123, "v1:Key" => "api" })
# => "<findUser><id>123</id><v1:Key>api</v1:Key></findUser>"

Build status Gem Version Code Climate Coverage Status

Installation

Gyoku is available through Rubygems and can be installed via:

$ gem install gyoku

or add it to your Gemfile like this:

gem 'gyoku', '~> 1.0'

Hash keys

Hash key Symbols are converted to lowerCamelCase Strings.

Gyoku.xml(:lower_camel_case => "key")
# => "<lowerCamelCase>key</lowerCamelCase>"

You can change the default conversion formula to :camelcase, :upcase or :none.
Note that options are passed as a second Hash to the .xml method.

Gyoku.xml({ :camel_case => "key" }, { :key_converter => :camelcase })
# => "<CamelCase>key</CamelCase>"

Custom key converters. You can use a lambda/Proc to provide customer key converters. This is a great way to leverage active support inflections for domain specific acronyms.

# Use camelize lower which will hook into active support if installed.
Gyoku.xml({ acronym_abc: "value" }, key_converter: lambda { |key| key.camelize(:lower) })
# => "<acronymABC>value</acronymABC>"

Hash key Strings are not converted and may contain namespaces.

Gyoku.xml("XML" => "key")
# => "<XML>key</XML>"

Hash values

  • DateTime objects are converted to xs:dateTime Strings
  • Objects responding to :to_datetime (except Strings) are converted to xs:dateTime Strings
  • TrueClass and FalseClass objects are converted to "true" and "false" Strings
  • NilClass objects are converted to xsi:nil tags
  • These conventions are also applied to the return value of objects responding to :call
  • All other objects are converted to Strings using :to_s

Array values

Array items are by default wrapped with the containiner tag, which may be unexpected.

> Gyoku.xml({languages: [{language: 'ruby'},{language: 'java'}]})
# => "<languages><language>ruby</language></languages><languages><language>java</language></languages>"

You can set the unwrap option to remove this behavior.

> Gyoku.xml({languages: [{language: 'ruby'},{language: 'java'}]}, { unwrap: true})
# => "<languages><language>ruby</language><language>java</language></languages>"

Special characters

Gyoku escapes special characters unless the Hash key ends with an exclamation mark.

Gyoku.xml(:escaped => "<tag />", :not_escaped! => "<tag />")
# => "<escaped>&lt;tag /&gt;</escaped><notEscaped><tag /></notEscaped>"

Self-closing tags

Hash Keys ending with a forward slash create self-closing tags.

Gyoku.xml(:"self_closing/" => "", "selfClosing/" => nil)
# => "<selfClosing/><selfClosing/>"

Sort XML tags

In case you need the XML tags to be in a specific order, you can specify the order
through an additional Array stored under the :order! key.

Gyoku.xml(:name => "Eve", :id => 1, :order! => [:id, :name])
# => "<id>1</id><name>Eve</name>"

XML attributes

Adding XML attributes is rather ugly, but it can be done by specifying an additional
Hash stored under the:attributes! key.

Gyoku.xml(:person => "Eve", :attributes! => { :person => { :id => 1 } })
# => "<person id=\"1\">Eve</person>"

Explicit XML Attributes

In addition to using the :attributes! key, you may also specify attributes through keys beginning with an "@" sign. Since you'll need to set the attribute within the hash containing the node's contents, a :content! key can be used to explicity set the content of the node. The :content! value may be a String, Hash, or Array.

This is particularly useful for self-closing tags.

Using :attributes!

Gyoku.xml(
  "foo/" => "", 
  :attributes! => {
    "foo/" => {
      "bar" => "1", 
      "biz" => "2", 
      "baz" => "3"
    }
  }
)
# => "<foo baz=\"3\" bar=\"1\" biz=\"2\"/>"

Using "@" keys and ":content!"

Gyoku.xml(
  "foo/" => {
    :@bar => "1",
    :@biz => "2",
    :@baz => "3",
    :content! => ""
  })
# => "<foo baz=\"3\" bar=\"1\" biz=\"2\"/>"

Example using "@" to get Array of parent tags each with @attributes & :content!

Gyoku.xml(
  "foo" => [
    {:@name => "bar", :content! => 'gyoku'}
    {:@name => "baz", :@some => "attr", :content! => 'rocks!'}
  ])
# => "<foo name=\"bar\">gyoku</foo><foo name=\"baz\" some=\"attr\">rocks!</foo>"

Unwrapping Arrays. You can specify an optional unwrap argument to modify the default Array behavior. unwrap accepts a boolean flag (false by default) or an Array whitelist of keys to unwrap.

# Default Array behavior
Gyoku.xml({
  "foo" => [
    {:is => 'great' },
    {:is => 'awesome'}
  ]
})
# => "<foo><is>great</is></foo><foo><is>awesome</is></foo>"

# Unwrap Array behavior
Gyoku.xml({
  "foo" => [
    {:is => 'great' },
    {:is => 'awesome'}
  ]
}, unwrap: true)
# => "<foo><is>great</is><is>awesome</is></foo>"

# Unwrap Array, whitelist.
# foo is not unwrapped, bar is.
Gyoku.xml({
  "foo" => [
    {:is => 'great' },
    {:is => 'awesome'}
  ],
  "bar" => [
      {:is => 'rad' },
      {:is => 'cool'}
  ]
}, unwrap: [:bar])
# => "<foo><is>great</is></foo><foo><is>awesome</is></foo><bar><is>rad</is><is>cool</is></bar>"

Naturally, it would ignore :content! if tag is self-closing:

Gyoku.xml(
  "foo/" => [
    {:@name => "bar", :content! => 'gyoku'}
    {:@name => "baz", :@some => "attr", :content! => 'rocks!'}
  ])
# => "<foo name=\"bar\"/><foo name=\"baz\" some=\"attr\"/>"

This seems a bit more explicit with the attributes rather than having to maintain a hash of attributes.

For backward compatibility, :attributes! will still work. However, "@" keys will override :attributes! keys if there is a conflict.

Gyoku.xml(:person => {:content! => "Adam", :@id! => 0})
# => "<person id=\"0\">Adam</person>"

Example with ":content!", :attributes! and "@" keys

Gyoku.xml({ 
  :subtitle => { 
    :@lang => "en", 
    :content! => "It's Godzilla!" 
  }, 
  :attributes! => { :subtitle => { "lang" => "jp" } } 
}
# => "<subtitle lang=\"en\">It's Godzilla!</subtitle>"

The example above shows an example of how you can use all three at the same time.

Notice that we have the attribute "lang" defined twice. The @lang value takes precedence over the :attribute![:subtitle]["lang"] value.

Pretty Print

You can prettify the output XML to make it more readable. Use these options:

  • pretty_print โ€“ controls pretty mode (default: false)
  • indent โ€“ specifies indentation in spaces (default: 2)
  • compact โ€“ย controls compact mode (default: true)

This feature is not available for XML documents generated from arrays with unwrap option set to false as such documents are not valid

Examples

puts Gyoku.xml({user: { name: 'John', job: { title: 'Programmer' }, :@status => 'active' }}, pretty_print: true)
#<user status='active'>
#  <name>John</name>
#  <job>
#    <title>Programmer</title>
#  </job>
#</user>
puts Gyoku.xml({user: { name: 'John', job: { title: 'Programmer' }, :@status => 'active' }}, pretty_print: true, indent: 4)
#<user status='active'>
#    <name>John</name>
#    <job>
#        <title>Programmer</title>
#    </job>
#</user>
puts Gyoku.xml({user: { name: 'John', job: { title: 'Programmer' }, :@status => 'active' }}, pretty_print: true, compact: false)
#<user status='active'>
#  <name>
#    John
#  </name>
#  <job>
#    <title>
#      Programmer
#    </title>
#  </job>
#</user>

Generate XML from an array with unwrap option set to true

puts Gyoku::Array.to_xml(["john", "jane"], "user", true, {}, pretty_print: true, unwrap: true)
#<user>
#  <user>john</user>
#  <user>jane</user>
#</user>

Generate XML from an array with unwrap option unset (false by default)

puts Gyoku::Array.to_xml(["john", "jane"], "user", true, {}, pretty_print: true)
#<user>john</user><user>jane</user>