Ruby literal representation of XML documents in the style of SXML
Installation
$ gem install rsxml
Background
Rsxml is a Ruby library to translate XML documents into an s-expression style Ruby literal representation, using Array and Hash literals, and back again
Why would you want to do this ? Ruby literals :
- can be == compared natively
- are easy to read
- can be indented nicely by editors
- can be syntax checked and balanced by editors
These features make them nice for writing readable XML generation code and readable tests for XML generating code
Rsxml uses Nokogiri for parsing XML, and Builder for generating it. Rsxml is not a feature complete XML processor : It does not attempt to process PIs, CDATA etc, but it does make it very easy to use and generate straightforward XML documents from Ruby
Use
Rsxml represents XML documents as s-expressions comprised of Ruby Array and Hash literals, thus :
["Foo", {"foofoo"=>"10"}, ["Bar", "barbar"], ["Baz"]]
represents the XML document :
<Foo foofoo="10"><Bar>barbar</Bar><Baz></Baz></Foo>
It is easy to convert XML docuemnts to Rsxml representation and back again :
xml = Rsxml.to_xml(["Foo", {"foofoo"=>"10"}, ["Bar", "barbar"]])
=> '<Foo foofoo="10"><Bar>barbar</Bar></Foo>'
Rsxml.to_rsxml(xml)
=> ["Foo", {"foofoo"=>"10"}, ["Bar", "barbar"]]
Namespaces
XML namespaces are dealt with straightforwardly. When an XML document is converted to Rsxml, namespaces are preserved, and you can specify namespaces in an Rsxml structure in two ways, which can be freely mixed
- using QName prefixes and declarative attributes, exactly as with XML
- using exploded QNames consisting of [local_part, prefix, uri] triples and [local_part, prefix] pairs
Converting to Rsxml
When you convert an XML document to Rsxml you can choose either :xml or :exploded style
:xml style
In :xml style namespaces are declared using attributes, and namespaces are referenced using
prefix:LocalPart
QNames, as in XML
Rsxml.to_rsxml('<foo:foofoo xmlns:foo="http://foo.com/foo" foo:bar="barbar"/>', :style=>:xml)
=> ["foo:foofoo", {"foo:bar"=>"barbar", "xmlns:foo"=>"http://foo.com/foo"}]
:exploded style
In :exploded style namespaces are not declared using attributes, and QNames are specified using [local_part, prefix, uri] triples
Rsxml.to_rsxml('<foo:foofoo xmlns:foo="http://foo.com/foo" foo:bar="barbar"/>', :style=>:exploded)
=> [["foofoo", "foo", "http://foo.com/foo"], {["bar", "foo", "http://foo.com/foo"]=>"barbar"}]
Converting to XML
Rsxml styles can be mixed, and replicated namespace references can be skipped (i.e. [local_part,prefix]
pairs can be used instead of [local_part, prefix, uri]
triples) for readability
Rsxml.to_xml([["foofoo", "foo", "http://foo.com/foo"], {"foo:bar"=>"1", ["baz", "foo"]=>"2"}])
=> '<foo:foofoo foo:baz="2" foo:bar="1" xmlns:foo="http://foo.com/foo"></foo:foofoo>'
Fragments
XML Fragments, without proper namespace declarations, can be parsed by passing a Hash of namespace prefix bindings
Rsxml.to_rsxml('<foo:foofoo foo:bar="barbar"/>', :ns=>{"foo"=>"http://foo.com/foo"}, :style=>:xml)
=> ["foo:foofoo", {"foo:bar"=>"barbar"}]
Fragments can be generated similarly :
Rsxml.to_xml(["foo:foofoo", {"foo:bar"=>"barbar"}], :ns=>{"foo"=>"http://foo.com/foo"})
=> '<foo:foofoo foo:bar="barbar"></foo:foofoo>'
Visitors
Rsxml.to_rsxml and Rsxml.to_xml are implemented with a simple Visitor pattern over the XML document structure. Rsxml::Sexp.traverse() traverses a Rsxml s-expression representation of an XML document and Rsxml::Xml.traverse() traverses a Nokogiri Node tree. The Visitor receives a
text(context, text)
invocation for each text node, and an
element(context, name, attrs, ns_decls)
method invocation for each element. If namespaces are used, element and attribute names in the element method invocations are exploded [local_part, prefix, uri] triples. The attributes are presented as a {name=>value} Hash
which contains no namespace-related attributes. Any namespace declarations for the element are provided as the {prefix=>uri} ns_decls
Hash
. Namespace prefixes, URIs and declaration attributes are cleanly separated in this API, so it is easy for Visitor implementations to correctly process XML documents with namespaces
Contributing to rsxml
- Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
- Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
- Fork the project
- Start a feature/bugfix branch
- Commit and push until you are happy with your contribution
- Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
- Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
Copyright
Copyright (c) 2012 mccraigmccraig of the clan mccraig. See LICENSE.txt for further details.