So I'm trying to build an XML file, and the nature of how it must be built means that I have to add elements outside of the natural nested structure. For example:
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.Data {
xml.Groups do |inner|
inner.send(:"GroupType", "test")
end
# Insert child element into Groups element.
}
end
I want the XML to look like:
<Data>
<Groups>
<GroupType>test</GroupType>
<AnotherNode>13</AnotherNode>
</Groups>
</Data>
Where <AnotherNode> is added where my comment is in the first code sample.
Should be relatively simple but I can't figure it out for the life of me. Presumably I need to be able to search for the block, or, have a reference to it when I form it and then use that?
On a side note, I've taken over a project that has a bunch of Nokogiri stuff in it already, it has selectors like:
xml_file = Nokogiri::XML(xml)
(xml_file/:RootNode).each do |root|
(root/:SomeItem).each do |si|
...
end
end
.. yet I can't find anything like that in the docs? what's it all about?
Bumped into this two years after the fact, and thought the accepted answer was way more convoluted than it needs to be.
The problem is, you're building an XML fragment here, while by default Nokogiri's builder assumes you're building a complete document. XML documents only have one root node.
One option would be to build the complete document as follows, then grab the root's children:
<DISPOSABLE_OUTER_NODE>
<Data>
<Groups>
<GroupType>test</GroupType>
</Groups>
</Data>
<AnotherNode>13</AnotherNode>
</DISPOSABLE_OUTER_NODE>
But there's a better, "fragmentary" way. You build an empty fragment, then have builder work on that rather than a new document:
frag = Nokogiri::XML::DocumentFragment.parse("")
Nokogiri::XML::Builder.with( frag ){ |b|
b.Data do
b.Groups do
b.GroupType "test"
end
end
b.AnotherNode "13"
}
puts frag.to_xml
Then just stick it into whatever document you're modifying.
Maybe with is a latecomer to Nokogiri. But it's an elegant solution to a problem I was having, so I thought it belonged here.
Using Nokogiri::XML::Builder only, you need to do it this way:
require 'nokogiri'
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.Data {
xml.Groups {
xml.GroupType "test"
xml.AnotherNode "13"
}
}
end
puts builder.to_xml
Which outputs:
=> <?xml version="1.0" encoding="UTF-8"?>
<Data>
<Groups>
<GroupType>test</GroupType>
<AnotherNode>13</AnotherNode>
</Groups>
</Data>
Builder is a DSL and designed as a convenience, with a limited set of capabilities. If you don't want to do it the "Builder-way" you can do it "old-school" and take an existing XML Node, and build upon it:
require 'nokogiri'
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.Data {
xml.Groups {
xml.GroupType "test"
}
}
end
This created the base XML needed inside the Builder object. Render it as XML and reparse it into a Nokogiri::XML::Document, then work on it:
doc = Nokogiri::XML(builder.to_xml)
doc.at('GroupType').add_next_sibling("<AnotherNode>13</AnotherNode>")
puts doc.to_xml
=> <?xml version="1.0" encoding="UTF-8"?>
<Data>
<Groups>
<GroupType>test</GroupType><AnotherNode>13</AnotherNode>
</Groups>
</Data>
doc = Nokogiri::XML(builder.to_xml)
doc.at('Groups').add_child("<AnotherNode>13</AnotherNode>")
puts doc.to_xml
=> <?xml version="1.0" encoding="UTF-8"?>
<Data>
<Groups>
<GroupType>test</GroupType>
<AnotherNode>13</AnotherNode></Groups>
</Data>
Either of the two above ways render the same thing syntactically, they're just cosmetically different.
You can even get convoluted and funky and do it this way:
builder = Nokogiri::XML::Builder.with(
Nokogiri::XML(
builder.to_xml
).at('Groups') << "<AnotherNode>13</AnotherNode>"
)
puts builder.to_xml
=> <?xml version="1.0" encoding="UTF-8"?>
<Data>
<Groups>
<GroupType>test</GroupType>
<AnotherNode>13</AnotherNode></Groups>
</Data>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With