2011-03-15 29 views
6

Tengo un simple archivo XML, items.xml:Cómo añadir un nuevo nodo a 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> 

Estoy tratando de insertar un nuevo nodo con el siguiente código:

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 

Sin embargo este código añade un nuevo nodo sólo después de la última <manufacturer> nodo, 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> 

¿Por qué?

+0

Lo haces demasiado difícil. Nokogiri facilita agregar nodos usando una cadena, que luego analiza en un nuevo nodo. Ver mi respuesta a continuación. –

Respuesta

12

Sería útil distinguir entre Node (una pieza particular de datos XML estructurados en un lugar particular en un árbol) y una "plantilla de nodos" que es la estructura de los datos.

Nokogiri (y la mayoría de las otras bibliotecas XML) solo le permiten especificar Node s, no plantillas de nodos. Entonces, cuando creaste price = Nokogiri::XML::Node.new "price", @items, tenías un dato particular que pertenece a un lugar en particular, pero aún no había definido el lugar.

Cuando lo agregó al primer <item>, definió su lugar. Cuando lo agregó al segundo <item>, lo arrancó de su lugar y lo colocó en un lugar nuevo. En ese punto, este Node apareció solo en el segundo <item>. Esto continúa cuando agrega el mismo Node a cada elemento, hasta llegar al último <item>, que es donde permanece el nodo.

Nokogiri no tiene ninguna forma de especificar una plantilla de nodo. Lo que hay que hacer es:

@items.xpath('//items/item/manufacturer').each do |node| 
    price = Nokogiri::XML::Node.new "price", @items 
    price.content = "10" 
    node.add_next_sibling(price) 
end 
+0

Vi el comportamiento exacto que explicó en el depurador, pero no pude entenderlo. ¡Gracias por tu explicación y tu respuesta! –

+2

Alternativamente, puede usar simplemente ['Node # dup'] (http://nokogiri.rubyforge.org/nokogiri/Nokogiri/XML/Node.html#M000469) para clonar el nodo antes de insertarlo en una nueva ubicación . – Phrogz

+0

Buena explicación ... Estaba teniendo el mismo problema desde hace 3-4 días. Ahora descubrí por qué estaba sucediendo. Gracias por el OP y por ti. –

2

me gustaría empezar con esto:

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>') } 

que se traduce en:

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> 

Es fácil de construir sobre este insertar valores diferentes para el precio.

Cuestiones relacionadas