Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add a new node to XML

Tags:

xml

ruby

nokogiri

I have a simple XML file, items.xml:

 <?xml version="1.0" encoding="UTF-8" ?>

<items>
  <item>
    <name>mouse</name>
    <manufacturer>Logicteh</manufacturer>
  </item>
  <item>
    <name>keyboard</name>
    <manufacturer>Logitech - Inc.</manufacturer>
  </item>
  <item>
    <name>webcam</name>
    <manufacturer>Logistech</manufacturer>
  </item>
</items>

I am trying to insert a new node with the following code:

require 'rubygems'
require 'nokogiri'

f = File.open('items.xml')
@items = Nokogiri::XML(f)
f.close

price = Nokogiri::XML::Node.new "price", @items
price.content = "10"

@items.xpath('//items/item/manufacturer').each do |node|
  node.add_next_sibling(price)
end

file = File.open("items_fixed.xml",'w')
file.puts @items.to_xml
file.close

However this code adds a new node only after the last <manufacturer> node, items_fixed.xml:

<?xml version="1.0" encoding="UTF-8"?>
<items>
  <item>
    <name>mouse</name>
    <manufacturer>Logitech</manufacturer>
  </item>
  <item>
    <name>keyboard</name>
    <manufacturer>Logitech</manufacturer>
  </item>
  <item>
    <name>webcam</name>
    <manufacturer>Logitech</manufacturer><price>10</price>
  </item>
</items>

Why?

like image 414
Mr. L Avatar asked Mar 15 '11 18:03

Mr. L


People also ask

How do I add a node to an XML DOM?

XML DOM Add Nodes. 1 Try it Yourself - Examples. The examples below use the XML file books.xml. Add a node after the last child node. This example uses appendChild () to ... 2 Add a Node - appendChild () 3 Insert a Node - insertBefore () 4 Add a New Attribute. 5 Add Text to a Text Node - insertData ()

How to append a new element to a specific node in XML?

The below command will create a new XML element $newelement = $xmlfile.CreateElement("book") Once the element is created we need to append it to the specific node. Here we need to append a new element underneath the Catalog. $xmlfile.catalog.AppendChild($newelement) And the last part is to save the XML file using the below command.

How do I add a node to an existing node?

Add a Node - appendChild() The appendChild() method adds a child node to an existing node. The new node is added (appended) after any existing child nodes. Note: Use insertBefore() if the position of the node is important. This code fragment creates an element (<edition>), and adds it after the last child of the first <book> element:

How to add a node after the last child node?

The new node is added (appended) after any existing child nodes. Note: Use insertBefore () if the position of the node is important. This code fragment creates an element (<edition>), and adds it after the last child of the first <book> element:


2 Answers

It would be helpful to distinguish between a Node (a particular piece of structured XML data at a particular place in a tree), and a "node template" which is the structure of the data.

Nokogiri (and most other XML libraries) only allow you to specify Nodes, not node templates. So when you created price = Nokogiri::XML::Node.new "price", @items, you had a particular piece of data that belongs in a particular place, but hadn't defined the place yet.

When you added it to the first <item>, you defined its place. When you added it to the second <item>, you uprooted it from its place and put it in a new place. At that point this Node appeared only in the second <item>. This continues when you add the same Node to each item, until you reach the last <item>, which is where the node stays.

Nokogiri doesn't have any way to specify a node template. What you need to do is:

@items.xpath('//items/item/manufacturer').each do |node|
  price = Nokogiri::XML::Node.new "price", @items
  price.content = "10"
  node.add_next_sibling(price)
end
like image 162
Ken Bloom Avatar answered Oct 17 '22 19:10

Ken Bloom


I'd start with this:

require 'nokogiri'

doc = Nokogiri::XML(<<EOT)
<?xml version="1.0" encoding="UTF-8"?>
<items>
  <item>
    <name>mouse</name>
    <manufacturer>Logitech</manufacturer>
  </item>
  <item>
    <name>keyboard</name>
    <manufacturer>Logitech - Inc.</manufacturer>
  </item>
</items>
EOT

doc.search('manufacturer').each { |n| n.after('<price>10</price>') }

Which results in:

puts doc.to_xml
# >> <?xml version="1.0" encoding="UTF-8"?>
# >> <items>
# >>   <item>
# >>     <name>mouse</name>
# >>     <manufacturer>Logitech</manufacturer><price>10</price>
# >>   </item>
# >>   <item>
# >>     <name>keyboard</name>
# >>     <manufacturer>Logitech - Inc.</manufacturer><price>10</price>
# >>   </item>
# >> </items>

It's easy to build upon this to insert different values for the price.

like image 27
the Tin Man Avatar answered Oct 17 '22 20:10

the Tin Man