0

I'm doing this:

targets = @xml.xpath("./target")
if targets.empty?
  targets << Nokogiri::XML::Node.new('target', @xml)
end

However the @xml is still without my target. What can I do in order to update the original @xml?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Arnold Roa
  • 7,335
  • 5
  • 50
  • 69
  • Does [this question](http://stackoverflow.com/questions/939853/how-can-i-add-a-child-to-a-node-at-a-specific-position) answer your question? – tadman Jun 19 '15 at 19:56
  • You really should add sample XML that matches your code, along with what you expect the XML should look like. Don't ask us to cobble up sample data. – the Tin Man Jun 19 '15 at 20:00

2 Answers2

1

It's a lot easier than that:

require 'nokogiri'

doc = Nokogiri::XML(<<EOT)
<root>
  <node>
  </node>
</root>
EOT

doc.at('node').children = '<child>foo</child>'
doc.to_xml
# => "<?xml version=\"1.0\"?>\n<root>\n  <node><child>foo</child></node>\n</root>\n"

children= is smart enough to see what you're passing in and will do the dirty work for you. So just use a string to define the new node(s) and tell Nokogiri where to insert it.


doc.at('node').class   # => Nokogiri::XML::Element
doc.at('//node').class # => Nokogiri::XML::Element

doc.search('node').first   # => #<Nokogiri::XML::Element:0x3fd1a88c5c08 name="node" children=[#<Nokogiri::XML::Text:0x3fd1a88eda3c "\n  ">]>
doc.search('//node').first # => #<Nokogiri::XML::Element:0x3fd1a88c5c08 name="node" children=[#<Nokogiri::XML::Text:0x3fd1a88eda3c "\n  ">]>

search is the generic "find a node" method that will take either CSS or XPath selectors. at is equivalent to search('some selector').first. at_css and at_xpath are the specific equivalents of at, just as css and xpath are to search. Use the specific versions if you want, but in general I use the generic versions.


You can't use:

targets = @xml.xpath("./target")
if targets.empty?
  targets << Nokogiri::XML::Node.new('target', @xml)
end

targets would be [] (actually an empty NodeSet) if ./target doesn't exist in the DOM. You can't append a node to [], because NodeSet doesn't have an idea of what you're talking about, resulting in a undefined method 'children=' for nil:NilClass (NoMethodError) exception.

Instead you MUST find the specific location where you want to insert the node. at is good for that since it finds just the first location. Of course, if you want to look for multiple places to modify something use search then iterate over the returned NodeSet and modify based on the individual nodes returned.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • It's not possible to do with xpath? thing is that I'm receiving an on @xml and can modify it... – Arnold Roa Jun 19 '15 at 20:00
  • It doesn't matter whether you use `xpath` or `css` or `at` or any of the "find a node" methods. What matters is how you add it. – the Tin Man Jun 19 '15 at 20:01
  • I'm choosing your answer as correct as you gave a lot of useful information, but please check the answer you see any downside of doing that? – Arnold Roa Jun 20 '15 at 12:23
  • Try it and see. I don't think it's going to work but you need to understand why. – the Tin Man Jun 21 '15 at 05:09
0

I end doing this, works fine.

targets = @xml.xpath("./target")
if targets.empty?
  targets << Nokogiri::XML::Node.new('target', @xml)
  @xml.add_child(targets.first)
end
Arnold Roa
  • 7,335
  • 5
  • 50
  • 69