Вставка и удаление узлов и элементов XML с помощью Nokogiri
Я хочу извлечь часть XML файла и сделать заметку о том, что я извлек какую-то часть в этом файле, например "здесь что-то было извлечено".
Я пытаюсь сделать это с помощью Nokogiri, но, похоже, на самом деле не документировано, как:
- удалить все дочерние элементы
<Nokogiri::XML::Element>
- измените
inner_text
этого полного элемента
Любые подсказки?
Ответы
Ответ 1
Nokogiri делает это довольно легко. Используя этот документ в качестве примера, следующий код найдет все теги vitamins
, удалит их детей (и детей-детей и т.д.), и изменить их внутренний текст, чтобы сказать "Дети удалены".
require 'nokogiri'
io = File.open('sample.xml', 'r')
doc = Nokogiri::XML(io)
io.close
doc.search('//vitamins').each do |node|
node.children.remove
node.content = 'Children removed.'
end
Данный food
node будет выглядеть следующим образом:
<food>
<name>Avocado Dip</name>
<mfr>Sunnydale</mfr>
<serving units="g">29</serving>
<calories total="110" fat="100"/>
<total-fat>11</total-fat>
<saturated-fat>3</saturated-fat>
<cholesterol>5</cholesterol>
<sodium>210</sodium>
<carb>2</carb>
<fiber>0</fiber>
<protein>1</protein>
<vitamins>
<a>0</a>
<c>0</c>
</vitamins>
<minerals>
<ca>0</ca>
<fe>0</fe>
</minerals>
</food>
:
<food>
<name>Avocado Dip</name>
<mfr>Sunnydale</mfr>
<serving units="g">29</serving>
<calories total="110" fat="100"/>
<total-fat>11</total-fat>
<saturated-fat>3</saturated-fat>
<cholesterol>5</cholesterol>
<sodium>210</sodium>
<carb>2</carb>
<fiber>0</fiber>
<protein>1</protein>
<vitamins>Children removed.</vitamins>
<minerals>
<ca>0</ca>
<fe>0</fe>
</minerals>
</food>
Ответ 2
Вы можете сделать это следующим образом:
doc=Nokogiri::XML(your_document)
note=doc.search("note") # find all tags with the node_name "note"
note.remove
Хотя это приведет к удалению всех дочерних элементов в теге <note>
, я не уверен, как "изменить внутренний_текст" всех элементов заметки. Я думаю, что inner_text
не применим для Nokogiri:: XML:: Element.
Ответ 3
Предыдущий пример Nokogiri поставил меня в правильном направлении, но с помощью doc.search
осталось неправильное //vitamins
, поэтому я использовал CSS:
require "rubygems"
require "nokogiri"
f = File.open("food.xml")
doc = Nokogiri::XML(f)
doc.css("food vitamins").each do |node|
puts "\r\n[debug] Before: vitamins= \r\n#{node}"
node.children.remove
node.content = "Children removed"
puts "\r\n[debug] After: vitamins=\r\n#{node}"
end
f.close
Результат:
debug] Before: vitamins=
<vitamins>
<a>0</a>
<c>0</c>
</vitamins>
[debug] After: vitamins=
<vitamins>Children removed</vitamins>
Ответ 4
Вот что я сделал бы:
Сначала проанализируйте XML:
require 'nokogiri'
doc = Nokogiri::XML(<<EOT)
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="nutrition.css"?>
<nutrition>
<daily-values>
<total-fat units="g">65</total-fat>
<saturated-fat units="g">20</saturated-fat>
<cholesterol units="mg">300</cholesterol>
<sodium units="mg">2400</sodium>
<carb units="g">300</carb>
<fiber units="g">25</fiber>
<protein units="g">50</protein>
</daily-values>
<food>
<name>Avocado Dip</name>
<mfr>Sunnydale</mfr>
<serving units="g">29</serving>
<calories total="110" fat="100"/>
<total-fat>11</total-fat>
<saturated-fat>3</saturated-fat>
<cholesterol>5</cholesterol>
<sodium>210</sodium>
<carb>2</carb>
<fiber>0</fiber>
<protein>1</protein>
<vitamins>
<a>0</a>
<c>0</c>
</vitamins>
<minerals>
<ca>0</ca>
<fe>0</fe>
</minerals>
</food>
</nutrition>
EOT
Если я хочу удалить содержимое node, я могу удалить его children
или назначить nil его содержимому:
doc.at('total-fat').to_xml # => "<total-fat units=\"g\">65</total-fat>"
doc.at('total-fat').children.remove
doc.at('total-fat').to_xml # => "<total-fat units=\"g\"/>"
или
doc.at('saturated-fat').to_xml # => "<saturated-fat units=\"g\">20</saturated-fat>"
doc.at('saturated-fat').content = nil
doc.at('saturated-fat').to_xml # => "<saturated-fat units=\"g\"/>"
Если я хочу извлечь текст из node для использования другим способом:
food = doc.at('food').text
# => "\n Avocado Dip\n Sunnydale\n 29\n \n 11\n 3\n 5\n 210\n 2\n 0\n 1\n \n 0\n 0\n \n \n 0\n 0\n \n "
или
food = doc.at('food').children.map(&:text)
# => ["\n ",
# "Avocado Dip",
# "\n ",
# "Sunnydale",
# "\n ",
# "29",
# "\n ",
# "",
# "\n ",
# "11",
# "\n ",
# "3",
# "\n ",
# "5",
# "\n ",
# "210",
# "\n ",
# "2",
# "\n ",
# "0",
# "\n ",
# "1",
# "\n ",
# "\n 0\n 0\n ",
# "\n ",
# "\n 0\n 0\n ",
# "\n "]
или, тем не менее, вы хотите калечить текст.
И, если вы хотите отметить, что вы удалили текст:
doc.at('food').content = 'REMOVED'
doc.at('food').to_xml # => "<food>REMOVED</food>"
Вместо этого вы можете использовать комментарий XML:
doc.at('food').children = '<!-- REMOVED -->'
doc.at('food').to_xml # => "<food>\n <!-- REMOVED -->\n</food>"