muski muski -4 years ago 80
Ruby Question

How to update a NodeSet

I am trying to add one more

element in the following XML:

<EventRouter id="Important Events to Log File" class="com.cyclonecommerce.events2.router.LogFileRouter" active="true">
<Parameters file="../logs/%nodeName%_events.log"
rollOnStart="false" autoFlush="true"
maxFileSize="2M" maxBackupFiles="5"/>
<MetadataProcessorListRef ref="Messaging"/>
<EventFilterRef ref="Important"/>
<EventRouter id="Message Detail to Log File" class="com.cyclonecommerce.events2.router.LogFileRouter" active="false">
<Parameters file="../logs/%nodeName%_message_detail.log"
rollOnStart="true" autoFlush="true"
maxFileSize="2M" maxBackupFiles="5"/>
<MetadataProcessorListRef ref="Messaging"/>
<EventFilterRef ref="Message Detail"/>


I am able to append a node to a nodeset but after saving the XML I am not getting the updated XML I want:

require 'nokogiri'

if File.exist?('a.xml')
data ="a.xml")

doc = Nokogiri::XML.parse data

event_router ="EventRouter",doc)
event_router['id'] = "some"
param_node ="Parameters",doc)
param_node.content = "some content"
event_router << param_node
doc2<< event_router'test.xml', 'w') do |file|
file.print doc.to_xml

Answer Source

Adding new nodes is simple. The basic idea is similar to parsing XML or HTML:

  1. Find the node where you want to add new content.
  2. Use the appropriate method to append the content, either at the start of the children of the node, or as a sibling or a parent wrapping the node. Those methods are in the Node documentation.
  3. You're done.

Nokogiri makes it easy to add a node using a string, which it will parse and convert to a Node:

require 'nokogiri'
doc = Nokogiri::XML('<xml><node/></xml>')

node ='node')
doc.to_xml # => "<?xml version=\"1.0\"?>\n<xml>\n  <node>\n    <foo>bar</foo>\n  </node>\n</xml>\n"

You can go the longer route of creating a new Node but I find building a string easier.

Using your code for the basis, here's how I'd have written it:

require 'nokogiri'

data =

doc = Nokogiri::XML.parse data
doc.xpath("//EventRouters/EventRouter").each do |event_router|
  event_router.add_next_sibling("<EventRouter id='some'><Parameters>some content</Parameters></EventRouter>")

puts doc.to_xml


Running that results in:

<?xml version="1.0"?>
  <EventRouter>original1</EventRouter><EventRouter id="some"><Parameters>some content</Parameters></EventRouter>
  <EventRouter>original2</EventRouter><EventRouter id="some"><Parameters>some content</Parameters></EventRouter>

You're trying to use << to append to a NodeSet, which isn't going to do what you think it will:

doc = Nokogiri::XML.parse data
ers ='EventRouter') 
ers <<'<foo/>', doc)

Appending to ers, which is a NodeSet, won't apply the new Node to all the elements in the set:

doc.to_xml # => "<?xml version=\"1.0\"?>\n<EventRouters>\n  <EventRouter>original1</EventRouter>\n  <EventRouter>original2</EventRouter>\n</EventRouters>\n"

Instead it appends it to the end of the set:

ers.to_xml # => "<EventRouter>original1</EventRouter><EventRouter>original2</EventRouter><<foo/>/>"
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download