
Security News
High Salaries No Longer Enough to Attract Top Cybersecurity Talent
A survey of 500 cybersecurity pros reveals high pay isn't enough—lack of growth and flexibility is driving attrition and risking organizational security.
license: MIT
Convert Ruby Hashes to XML
A hash is meant to be a structured set of data. So is XML. The two are very similar in that they have the capability of nesting information within a tree structure. With XML you have nodes. With Hashes, you have key/value pairs. The value of an XML node is referenced by its parent's name. A hash value is referenced by its key. This basic lesson tells the majority of what you need to know about creating XML via Hashes in Ruby using the XmlFu gem.
Add this line to your application's Gemfile:
gem 'xml-fu'
And then execute:
$ bundle
Or install it yourself as:
$ gem install xml-fu
Configuration was reworked to be more flexible and provide a centralized object for configuration. As such, the configuration options have been moved off of the XmlFu module into the XmlFu.config object. The XmlFu.configure method still works the same for setting configurations, but for reading configuration variables, you need to go through the XmlFu.config object.
Hash keys are translated into XML nodes (whether it be a document node or attribute node).
With Ruby, a hash key may be a string or a symbol. Strings will be preserved as they may contain node namespacing ("foo:Bar" would need preserved rather than converted). There are some exceptions to this rule (especially with special key syntax discussed in Types of Nodes and the like). Symbols will be converted into an XML safe name by lower camel-casing them. So :foo_bar will become "fooBar". You may change the conversion algorithm to your liking by setting the XmlFu.config.symbol_conversion_algorithm to a lambda or proc of your liking.
For a complete list, reference XmlFu::Configuration::ALGORITHMS
# Default Algorithm (:lower_camelcase)
XmlFu.xml( :FooBar => "bang" ) #=> "<fooBar>bang</fooBar>"
XmlFu.xml( :foo_bar => "bang" ) #=> "<fooBar>bang</fooBar>"
# Built-in Algorithms (:camelcase)
XmlFu.config.symbol_conversion_algorithm = :camelcase
XmlFu.xml( :Foo_Bar => "bang" ) #=> "<FooBar>bang</FooBar>"
XmlFu.xml( :foo_bar => "bang" ) #=> "<FooBar>bang</FooBar>"
# Built-in Algorithms (:downcase)
XmlFu.config.symbol_conversion_algorithm = :downcase
XmlFu.xml( :foo_bar => "bang" ) #=> "<foo_bar>bang</foo_bar>"
XmlFu.xml( :Foo_Bar => "bang" ) #=> "<foo_bar>bang</foo_bar>"
XmlFu.xml( :FOO => "bar" ) #=> "<foo>bar</foo>"
# Built-in Algorithms (:upcase)
XmlFu.config.symbol_conversion_algorithm = :upcase
XmlFu.xml( :foo_bar => "bang" ) #=> "<FOO_BAR>bang</FOO_BAR>"
XmlFu.xml( :Foo_Bar => "bang" ) #=> "<FOO_BAR>bang</FOO_BAR>"
XmlFu.xml( :foo => "bar" ) #=> "<FOO>bar</FOO>"
# Custom Algorithm
XmlFu.config.symbol_conversion_algorithm = lambda {|sym| sym.do_something_special }
Because there are multiple types of XML nodes, there are also multiple types of keys to denote them.
By default, XmlFu assumes that all XML nodes will contain closing tags. However, if you want to explicitly create a self-closing node, use the following syntax when you define the key.
XmlFu.xml("foo/" => "bar") #=> <foo/>
One thing to take note of this syntax is that XmlFu will ignore ANY value you throw at it if the key syntax denotes a self-closing tag. This is because a self-closing tag cannot have any contents (hence the use for a self-closing tag).
By default, if you pass a pure string as a value, special characters will be escaped to keep the XML compliant. If you know that the string is valid XML and can be trusted, you can add the exclamation point to the end of the key name to denote that XmlFu should NOT escape special characters in the value.
# Default Functionality (Escaped Characters)
XmlFu.xml("foo" => "<bar/>") #=> "<foo><bar/></foo>"
# Unescaped Characters
XmlFu.xml("foo!" => "<bar/>") #=> "<foo><bar/></foo>"
Yes, the attributes of an XML node are nodes themselves, so we need a way of defining them. Since XPath syntax uses @ to denote an attribute, so does XmlFu.
Note: Keep in mind, because of the way that XML::Builder accepts an attributes hash and the way that Ruby stores and retrieves values from a Hash, that attributes won't always be generated in the same order. We can only guarantee that the attributes will be present, not in any specific order.
XmlFu.xml(:agent => {
"@id" => "007",
"FirstName" => "James",
"LastName" => "Bond"
})
#=> <agent id="007"><FirstName>James</FirstName><LastName>Bond</LastName></agent>
The value in a key/value pair describes the key/node. Different value types determine the extent of this description.
Simple value types describe the contents of the XML node.
XmlFu.xml( :foo => "bar" ) #=> "<foo>bar</foo>"
XmlFu.xml( "foo" => "bar" ) #=> "<foo>bar</foo>"
XmlFu.xml( :foo => 0 ) #=> "<foo>0</foo>"
XmlFu.xml( :pi => 3.14159 ) #=> "<pi>3.14159</pi>"
XmlFu.xml( :foo => nil ) #=> "<foo xsi:nil=\"true\"/>"
Hash are parsed for their translated values prior to returning a XmlFu value.
XmlFu.xml(:foo => {:bar => {:biz => "bang"} })
#=> "<foo><bar><biz>bang</biz></bar></foo>"
Should you require setting node attributes as well as setting the value of the XML node, you may use the "=" key in a nested hash to denote explicit content.
XmlFu.xml(:agent => {"@id" => "007", "=" => "James Bond"})
#=> "<agent id=\"007\">James Bond</agent>"
This key will not get around the self-closing node rule. The only nodes that will be used in this case will be attribute nodes and additional content will be ignored.
XmlFu.xml("foo/" => {"@id" => "123", "=" => "You can't see me."})
#=> "<foo id=\"123\"/>"
Since version 0.2.0, support has been added for converting an OpenStruct object. OpenStruct objects behave similar to Hashes, but they do not allow the flexibility that Hashes provide when naming keys/methods. As such, the advanced naming capabilities are not available with OpenStruct objects and key conversion will go through XmlFu.config.symbol_conversion_algorithm.
Since the value in a key/value pair is (for the most part) used as the contents of a key/node, there are some assumptions that XmlFu makes when dealing with Array values.
XmlFu.xml( "SecretAgents" => [
{ "agent/" => { "@id"=>"006", "@name"=>"Alec Trevelyan" } },
{ "agent/" => { "@id"=>"007", "@name"=>"James Bond" } }
])
#=> "<SecretAgents><agent name=\"Alec Trevelyan\" id=\"006\"/><agent name=\"James Bond\" id=\"007\"/></SecretAgents>"
There comes a time that you may want to declare the contents of an array as a collection of items denoted by the key name. Using the asterisk (also known for multiplication --- hence multiple keys) we denote that we want a collection of <key> nodes.
XmlFu.xml( "person*" => ["Bob", "Sheila"] )
#=> "<person>Bob</person><person>Sheila</person>"
In this case, the value of "person*" is an array of two names. These names are to be the contents of multiple <person> nodes and the result is a set of sibling XML nodes with no parent.
How about a more complex example:
XmlFu.xml(
"person*" => {
"@foo" => "bar",
"=" => [
{"@foo" => "nope", "=" => "Bob"},
"Sheila"
]
}
)
#=> "<person foo=\"nope\">Bob</person><person foo=\"bar\">Sheila</person>"
This is getting interesting, isn't it? In this example, we are setting a default "foo" attribute on each of the items in the collection of <person> nodes. However, you'll notice that we overwrote the default "foo" with Bob.
Array values are flattened prior to translation, to reduce the need to iterate over nested arrays.
XmlFu.xml(
:foo => [
[{"a/" => nil}, {"b/" => nil}],
{"c/" => nil},
[
[{"d/" => nil}, {"e/" => nil}],
{"f/" => nil}
]
]
)
#=> "<foo><a/><b/><c/><d/><e/><f/></foo>"
:foo in this case, is the parent node of it's contents
Since with simple values, you cannot infer the value of their node container purely on their value, simple values are currently ignored in arrays and only Hashes are translated.
"foo" => [
{:bar => "biz"},
nil, # ignored
true, # ignored
false, # ignored
42, # ignored
3.14, # ignored
"simple string", # ignored
['another','array','of','values'] # ignored
]
#=> "<foo><bar>biz</bar></foo>"
The following options are available to pass to XmlFu.xml(obj, options).
XmlFu.configure do |config|
config.symbol_conversion_algorithm = :default # (:lower_camelcase)
config.fail_on_invalid_construct = false # (false)
config.include_xml_declaration = nil # (nil)
end
This is used to convert symbol keys in a Hash to Strings.
When an unsupported object is passed to XmlFu.xml(), the default action is to return nil as the result. When fail_on_invalid_construct is enabled, XmlFu.xml() will raise an ArgumentError to denote that the passed object is not supported rather than fail silently.
Deals with adding/excluding <?xml version="1.0" encoding="UTF-8"?> to generated XML
git checkout -b my-new-feature
)git commit -am 'Added some feature'
)git push origin my-new-feature
)FAQs
Unknown package
We found that xml-fu demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
A survey of 500 cybersecurity pros reveals high pay isn't enough—lack of growth and flexibility is driving attrition and risking organizational security.
Product
Socket, the leader in open source security, is now available on Google Cloud Marketplace for simplified procurement and enhanced protection against supply chain attacks.
Security News
Corepack will be phased out from future Node.js releases following a TSC vote.