使用Ruby修改XML文件

时间:2014-07-31 11:37:03

标签: ruby xml-parsing nokogiri

考虑一个XML文档

 <string id = "id1" ><p> Text1 </p>
<p> Text 3 <\p>

</string>
    <string id = "id2" > Text2 </string>

我想更新字符串标签的内容,即将“Text1”替换为“Apple”,将“Text2”替换为“boy”。正如我在第一个语句中提到的那样,“Text1”没有直接封装在字符串标签中,它直接封装在其他标签中(这里是<p>但在输入文件中它可以是任意标签或{{1}内部1}}标签可以有一个标签,里面会有<p>

我试图这样做,但可以完成只更改“Text2”,因为它直接封装在字符串标记中

"Text1"

有人可以建议我在我的例子中修改“Text1”。


3 个答案:

答案 0 :(得分:0)

下面有一个想法,怎么做。此程序针对所有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>

答案 1 :(得分:0)

以下是使用xpath&#39; 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>

答案 2 :(得分:0)

因为其他两个答案都没有真正适用于所有操作的可能性,所以我修改了两个答案:

公共代码:

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)案例陈述的答案有点不同,因为从你的帖子中不清楚你是在搜索特定的文本,还是想用&#34; boy&#34;替换一个直接的文本节点,以及嵌套文本节点替换为&#34; Apple&#34;。

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>