Creating and Modifying XML Documents

Problem

You want to modify an XML document, or create a new one from scratch.

Solution

To create an XML document from scratch, just start with an empty Document object.

require exml/document require doc = REXML::Document.new

To add a new element to an existing document, pass its name and any attributes into its parents add_element method. You don have to create the Element objects yourself.

meeting = doc.add_element meeting meeting_start = Time.local(2006, 10, 31, 13) meeting.add_element( ime, { from => meeting_start, o => meeting_start + 3600 }) doc.children[0] # => … doc.children[0].children[0] # => "

To append a text node to the contents of an element, use the add_text method. This code adds an <agenda> element to the element, and gives it two different text nodes:

agenda = meeting.add_element agenda doc.children[1].children[1] # => <agenda/> agenda. add_text "Nothing of importance will be decided." agenda.add_text " The same tired ideas will be rehashed yet again." doc.children[1].children[1] # => <agenda> … doc.write($stdout, 1) # #

Element#text= is a nice shortcut for giving an element a single text node. You can also use to overwrite a documents initial text nodes:

item1 = agenda.add_element item doc.children[1].children[1].children[1] # => item1.text = Weekly status meetings: improving attendance doc.children[1].children[1].children[1] # => … doc.write($stdout, 1) # #

Discussion

If you can access an element or text node (numerically or with XPath), you can modify or delete it. You can modify an elements name with name=, and modify one of its attributes by assigning to an index of attributes. This code uses these methods to make major changes to a document:

doc = REXML::Document.new %{ } root = doc[1] # => … root.name = oy root.elements[//sugar].name = snails root.delete_element(//spice) set = root.elements[//set] set.attributes["of"] = "snips" set.attributes["cardinality"] = some root.add_element(set, {of => puppy dog tails, cardinality => some }) doc.write # # # # # # # #

You can delete an attribute with Element#delete_attribute, or by assigning nil to it:

root.attributes[size] = nil doc.write($stdout, 0) # # # # … #

You can use methods like replace_with to swap out one node for another:

doc.elements["//snails"].replace_with(REXML::Element.new("escargot"))

All these methods are convenient, but add_element in particular is not very idiomatic. The cgi library lets you structure method calls and code blocks so that your Ruby code has the same nesting structure as the HTML it generates. Why shouldn you be able to do the same for XML? Heres a new method for Element that makes it possible:

class REXML::Element def with_element(*args) e = add_element(*args) yield e if block_given? end end

Now you can structure your Ruby code the same way you structure your XML:

doc = REXML::Document.new doc.with_element(girl, {size => little}) do |girl| girl.with_element(foods) do |foods| foods.add_element(sugar) foods.add_element(spice) end girl.add_element(set, {of => ice things, cardinality => all}) end doc.write($stdout, 0) # # # # # # #

The builder gem also lets you build XML this way.

See Also

Категории