考虑一个XML文档
<string id = "id1" ><p> Text1 </p>
<p> Text 3 <p>
</string>
<string id = "id2" > Text2 </string>
我想更新字符串标签的内容,即将"Text1"替换为"Apple"one_answers"Text2"替换为"boy"。正如我在第一个声明中提到的"Text1"不是直接封装在字符串标签中,而是直接封装在其他标签中(这里是<p>
但在输入文件中,它可以是任何任意标签或者在<p>
标签中可以有一个标签其中将有"Text1"
我试图这样做,但可以完成改变只有"Text2",因为它是直接封装在字符串标签
require 'nokogiri'
doc = Nokogiri::XML(File.open("file.xml"));
result = {}
doc.xpath("//string").each do |node|
id_value = node.get_attribute "id"
puts "##############"
puts node
node.content = "a"
puts "%%%%%%%%%%%%%"
puts node
end
谁能告诉我一个方法来修改"Text1"在我的例子。
下面有一个想法,如何去做。该程序对所有string
节点进行迭代,当节点有非文本子节点时,它替换子节点内容。它适用于您的示例(注意,我必须用<xml>
标记包围XML),但是,再次将其视为一个想法。
require 'nokogiri'
xml = "<xml><string id = "id1" ><p> Text1 </p></string>n<string id = "id2" > Text2 </string></xml>"
doc = Nokogiri::XML.parse(xml)
doc.xpath('//string').each do |s|
case s.child
when Nokogiri::XML::Text
s.content = "boy"
when Nokogiri::XML::Element
s.child.content = "Apple"
end
end
puts doc.to_xml
输出:<?xml version="1.0"?>
<xml><string id="id1"><p>Apple</p></string>
<string id="id2">boy</string></xml>
使用xpath
的text()
和normalize-space()
:
doc.css("text()[normalize-space()='Text1']").each { |n| n.content = "Apple" }
doc.css("text()[normalize-space()='Text2']").each { |n| n.content = "boy" }
puts doc.to_s
# <?xml version="1.0"?>
# <xml><string id="id1"><p>Apple</p></string>
# <string id="id2">boy</string></xml>
由于其他两个答案实际上都不能正确地适用于所有op的可能性,因此我修改了两个答案:
共同代码:require 'nokogiri'
#Setting Nokogiri's parser options on the following line to strict(the default)
#and noblanks tells Nokogiri to ignore Text nodes that contain only whitespace:
xml_doc = Nokogiri::XML(<<END_OF_XML) { |config| config.strict.noblanks }
<root>
<not>Text1</not>
<string id = "id1" >
<p> Text1 </p>
<p> Text 3 </p>
</string>
<string id = "id2" > Text2 </string>
<string id="id3">
<p><p><p>Text1</p></p></p>
</string>
<not>Text2</not>
</root>
END_OF_XML
1)归一化空间的答案与:
new_xml = xml_doc.to_s.gsub('Text1', 'Apple').gsub('Text2', 'boy')
这里有一些更改,以便替换只发生在<string>
标签内:
xml_doc.xpath('//string').each do |string_tag|
string_tag.css(
"text()[normalize-space()='Text1']"
).each { |n| n.content = "Apple" }
string_tag.css(
"text()[normalize-space()='Text2']"
).each { |n| n.content = "boy" }
end
puts xml_doc.to_s
--output:--
<?xml version="1.0"?>
<root>
<not>Text1</not>
<string id="id1">
<p>Apple</p>
<p> Text 3 </p>
</string>
<string id="id2">boy</string>
<string id="id3">
<p><p><p>Apple</p></p></p>
</string>
<not>Text2</not>
</root>
你也可以这样写:
xml_doc.xpath("//string//text()[normalize-space()='Text1']"
).each { |n| n.content = "Apple" }
xml_doc.xpath("//string//text()[normalize-space()='Text2']"
).each { |n| n.content = "boy" }
puts xml_doc.to_s
但是您必须搜索整个xml_doc两次,并且我认为同时搜索两个文本的每个字符串标记可能更有效。
最初的答案也使用了未记录的(据我所知)css()方法的xpath。根据文档,css()的参数需要是css选择器,而xpath不是css选择器,所以使用xpath不应该起作用。
2) case语句的答案有点不同,因为从你的帖子中不清楚你是在搜索特定的文本,还是你想要一个直接的文本节点替换为"boy",一个嵌套的文本节点替换为"Apple"。
def get_base_text_node(node)
child_node = node.child
case child_node
when Nokogiri::XML::Text
child_node
when Nokogiri::XML::Element
get_base_text_node(child_node)
end
end
xml_doc.xpath('//string').each do |s|
case s.child
when Nokogiri::XML::Text
s.content = "boy"
else
text_node = get_base_text_node(s)
text_node.content = "Apple"
end
end
puts xml_doc.to_xml
--output:--
<?xml version="1.0"?>
<root>
<not>Text1</not>
<string id="id1">
<p>Apple</p>
<p> Text 3 </p>
</string>
<string id="id2">boy</string>
<string id="id3">
<p>
<p>
<p>Apple</p>
</p>
</p>
</string>
<not>Text2</not>
</root>