Creating XML nodes using Nokogiri
Asked Answered
F

2

5

I am having problems creating nodes and adding them to an XML file:

<mainnode>
    <secnode>
         <data1></data2>
         <data2></data2>
    </secnode>
</mainnode>

I want to be able to add to the file like so:

<mainnode>
    <secnode>
         <data1></data2>
         <data2></data2>
    </secnode>
    <secnode>
         <data1></data2>
         <data2></data2>
    </secnode>
</mainnode>

I am having trouble getting the concept of adding nodes with Nokogiri.

This is my current code:

def openXML
    f = File.open("file.xml")
    doc = Nokogiri::XML(f)
end

def parseXML
    mainnode.name = 'mainnode'
    f = openXML
    temp = Nokogiri::XML::Node.new "secnode", f
    mainnode.add_next_sibling(temp)

end

What concepts am I missing?

I need to be able to add instance variables to <data1> and <data2> but I found the Nokogiri tutorial to not be of much help in this area and have not made it past just adding the <secnode> node as a child of <mainnode>.

Any help is appreciated.

Furry answered 28/10, 2014 at 5:5 Comment(14)
What error are you getting from the above code?Miracle
also: what is inst ?Miracle
im getting an error inst is undefined. but my problem lies in that i don't know how to initialize inst as the parent node <mainnode> to append the child node <secnode>.Furry
you haven't defined an inst yet... ie there is no line of code that says inst = <something>Miracle
ohh sorry i changed the name for clarity from my actual code. editing nowFurry
so: the code you want is something like: inst = temp.<get _node('mainnode')> (where I don't know what the get_node function is called, but there must be one) right?Miracle
right that makes a bit more sense... so now there's no mainnode = <something> and it should be that line that is where you fetch the <mainnode> section from the xml? So something like mainnode = f.<get_node('mainnode')> ?Miracle
have you tried xpath? eg mainnode = f.xpath('//mainnode/') or similar? (note: not tested)Miracle
the .add_next_sibling method is not a method of xpath classFurry
xpath returns an array. cannot append a node to it.Furry
so, grab the first element out of that array?Miracle
well in this case there would only be one element if i did an .xpath for <mainnode>. there is only one mainnode. but how then would i update my xml file to include additional <secnode>. if all i have is an array how then do i update my file? i could see parsing the file to string and then making a new file with the added <secnode> being done through string manipulation instead of appending nodes. But i feel like this is a step that is not best practice...Furry
if you can add a sibling... can you append a child? Once you have the mainnode - you append your secnode to it as a child node? I don't know what you intend to do with the final structure once you're don with it...Miracle
What you want to do isn't clear. Do you want to duplicate an existing node and its children? Do you want to add an additional node that is similar to the previous and just didn't change the tags or text making it apparent?Halfbeak
H
7

Adding nodes in Nokogiri is a lot easier than you think it is, but your question isn't very clear.

If you want to duplicate an existing node:

require 'nokogiri'

xml = <<EOT
<mainnode>
    <secnode>
         <data1></data2>
         <data2></data2>
    </secnode>
</mainnode>
EOT

doc = Nokogiri::XML(xml)

secnode = doc.at('secnode')
doc.root.add_child secnode.dup
puts doc.to_xml

Which, when run, results in:

<?xml version="1.0"?>
<mainnode>
    <secnode>
         <data1/>
         <data2/>
    </secnode>
<secnode>
         <data1/>
         <data2/>
    </secnode></mainnode>

The funky alignment is the result of appending the intervening text nodes used for indentation. The resulting XML is valid.

If you're adding a different set of nodes, it's still easy:

require 'nokogiri'

xml = <<EOT
<mainnode>
    <secnode>
         <data1></data2>
         <data2></data2>
    </secnode>
</mainnode>
EOT

node_to_add = <<EOT
<secnode>
  <data3 />
  <data4 />
</secnode>
EOT

doc = Nokogiri::XML(xml)

doc.root.add_child node_to_add
puts doc.to_xml

Which outputs:

<?xml version="1.0"?>
<mainnode>
    <secnode>
         <data1/>
         <data2/>
    </secnode>
<secnode>
  <data3/>
  <data4/>
</secnode>
</mainnode>

You can use that as a template:

require 'nokogiri'

xml = <<EOT
<mainnode>
    <secnode>
         <data1></data2>
         <data2></data2>
    </secnode>
</mainnode>
EOT

v1 = 'foo'
v2 = 'bar'
node_to_add = <<EOT
<secnode>
  <data3>#{ v1 }</data3>
  <data4>#{ v2 }</data4>
</secnode>
EOT

doc = Nokogiri::XML(xml)

doc.root.add_child node_to_add
puts doc.to_xml

Which looks like:

<?xml version="1.0"?>
<mainnode>
    <secnode>
         <data1/>
         <data2/>
    </secnode>
<secnode>
  <data3>foo</data3>
  <data4>bar</data4>
</secnode>
</mainnode>

Nokogiri makes it very easy to create nodes to be added by using string representations of the XML or HTML, which it then converts on the fly.

Halfbeak answered 28/10, 2014 at 19:37 Comment(4)
yea, way better than my 12 line implementation. Thank you for the insights!Furry
In the Nokogiri docs, you'll see node_or_tags as a parameter for various methods. That's how they describe using a string or text representation like I did.Halfbeak
So it's either or. I can append node to node, or string(tags) to node.Furry
Yes. I'm not sure what other thing you'd want to do. If you have a bunch of nodes already parsed, perhaps as a fragment, you can either do string manipulation via to_html or to_xml then play with it, or use them as is.Halfbeak
H
4
require 'nokogiri'

def parse_xml_file(file_name)
  f = File.read(file_name)
  Nokogiri::XML(f) # do not need variable here; it's the return value of the method
end

def add_element(doc, node_name)
  new_element = Nokogiri::XML::Node.new(node_name, doc)
  new_element.content = "anything"
  doc.root.add_child(new_element)
end

doc = parse_xml_file("sample.xml")
add_element(doc, 'secnode')

puts doc.to_s

Your line mainnode.name = 'mainnode' isn't doing anything and will throw an error; you haven't selected anything from the XML document yet.

You should read up on traversing an XML DOM and selecting nodes. Try this primer: https://blog.engineyard.com/2010/getting-started-with-nokogiri

You say you read the Nokogiri docs, but I'd go back and re-read this, too: http://www.nokogiri.org/tutorials/searching_a_xml_html_document.html

Finally, play around with nokogiri by parsing a doc in IRB and seeing what you can do with it. That's a good way to explore and get a feel for nokogiri.

Hallsy answered 28/10, 2014 at 13:35 Comment(1)
at first i was getting an issue. the only addition to the page after your code was </secnode>. My issue was i did not add any content to the node. after content was added with new_element.content it worked. good suggestions, i spent a couple hours in irb and more docs. Thank you.Furry

© 2022 - 2024 — McMap. All rights reserved.