Как добавить новый узел в XML - PullRequest
7 голосов
/ 15 марта 2011

У меня есть простой XML-файл 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>

Я пытаюсь вставить новый узел со следующим кодом:

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

Однако этот код добавляетновый узел только после последнего <manufacturer> узла 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>

Почему?

Ответы [ 2 ]

13 голосов
/ 15 марта 2011

Было бы полезно различить Node (конкретный фрагмент структурированных XML-данных в определенном месте дерева) и «шаблон узла», который представляет собой структуру данных.

Nokogiri (и большинство других XML-библиотек) позволяет указывать только Node s, а не шаблоны узлов. Поэтому, когда вы создали price = Nokogiri::XML::Node.new "price", @items, у вас был определенный фрагмент данных, который принадлежит определенному месту, но еще не определил это место.

Когда вы добавили его к первому <item>, вы определили его место. Когда вы добавили его ко второму <item>, вы вырвали его с места и поместили на новое место. В этот момент этот Node появился только во втором <item>. Это продолжается, когда вы добавляете один и тот же Node к каждому элементу, пока не достигнете последнего <item>, где находится узел.

У Нокогири нет способа указать шаблон узла. Что вам нужно сделать, это:

@items.xpath('//items/item/manufacturer').each do |node|
  price = Nokogiri::XML::Node.new "price", @items
  price.content = "10"
  node.add_next_sibling(price)
end
2 голосов
/ 20 августа 2016

Я бы начал с этого:

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

Что приводит к:

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>

Это легко построить, вставив разные значения для цены.

...