使用Ruby修改XML文件



考虑一个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>

使用xpathtext()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>

相关内容

  • 没有找到相关文章

最新更新