Search code examples
rubycopynokogiriparent

Nokogiri: copy node and add new parent to copy?


Any idea how to copy a node and then give it a new parent, with the goal of writing the copy to a new file?

I've noticed that when I reassign one node to be another's parent, nothing happens. For example,

doc.xpath("/child").each do|child|

  # copy node to new structure. also tried dup()
  copyofchild = child

  # create new node to become newdoc's parent
  mom = Nokogiri::XML::Node.new('mom', copyofchild)

  copyofchild.parent = mom

  puts copyofchild  # lists <child>...</child>, not <mom><child>...</child></mom>

  # write newdoc to file...   
end

The one example on the docs page shows something analogous working, although they're reassigning one item in a structure to be the parent of another item in the same structure.

Thanks!


Solution

  • Starting with this:

    require 'nokogiri'
    
    xml = '<xml><bar>text</bar></xml>'
    doc = Nokogiri::XML(xml)
    
    bar = doc.at('bar')
    bar.parent.children = '<foo>' + bar.to_xml + '</foo>'
    puts doc.to_xml
    

    Which looks like:

    <?xml version="1.0"?>
    <xml>
      <foo>
        <bar>text</bar>
      </foo>
    </xml>
    

    Alternately, you can do it like:

    bar = doc.at('bar')
    bar.replace('<foo>' + bar.to_xml + '</foo>')
    

    Part of the problem in your code is your XPath accessor:

    "/child"
    

    doesn't do what you think. It only finds a top-level <child> node, not one farther in the tree. In my example XML it'd be the equivalent to the <xml> node. Perhaps you want //child which finds <child> nodes throughout the document.

    Personally, I prefer CSS accessors over XPath. Both are nicely supported by Nokogiri, and both make some things easier than the other, so it's good to be familiar with both.