我有一个嵌套的XML文档,如下所示:
<?xml version="1.0"?>
<phone>
<name>test</name>
<descr>description</descr>
<empty/>
<lines>
<line>12345</line>
<css/>
</lines>
</phone>
我需要删除所有空的XML节点,例如<empty/>
和<css/>
。
我最终得到了类似的东西:
doc = Nokogiri::XML::DocumentFragment.parse <<-EOXML
<phone>
<name>test</name>
<descr>description</descr>
<empty/>
<lines>
<line>12345</line>
<css/>
</lines>
</phone>
EOXML
phone = doc.css("phone")
phone.children.each do | child |
child.remove if child.inner_text == ''
end
以上代码仅删除第一个空标记,例如<empty/>
。我无法进入嵌套块。我想我需要一些递归策略。我仔细阅读了Nokogiri文档并检查了很多例子,但我还没有找到解决方案。
我该如何解决这个问题?
我正在使用Ruby 1.9.3和Nokogiri 1.5.10。
答案 0 :(得分:2)
您应该能够使用xpath "/phone//*[not(text())]"
找到没有任何文本的所有节点。
require 'nokogiri'
doc = Nokogiri::XML::Document.parse <<-EOXML
<phone>
<name>test</name>
<descr>description</descr>
<empty/>
<lines>
<line>12345</line>
<css/>
</lines>
</phone>
EOXML
doc.xpath("/phone//*[not(text())]").remove
puts doc.to_s.gsub(/\n\s*\n/, "\n")
#=> <?xml version="1.0"?>
#=> <phone>
#=> <name>test</name>
#=> <descr>description</descr>
#=> <lines>
#=> <line>12345</line>
#=> </lines>
#=> </phone>
答案 1 :(得分:2)
具有不同方法的后来者,希望增加额外的洞察力。这种方法消除了烦人的额外新行,并为您提供了保留具有值设置属性的空字段的选项。
require 'nokogiri'
doc = Nokogiri::XML::Document.parse <<-EOXML
<phone>
<name>test</name>
<descr>description</descr>
<empty/>
<lines>
<line>12345</line>
<css/>
</lines>
</phone>
EOXML
def traverse_and_clean(kid)
kid.children.map { |child| traverse_and_clean(child) }
kid.remove if kid.content.blank?
end
traverse_and_clean(doc)
<强>输出强>
<?xml version="1.0"?>
<phone>
<name>test</name>
<descr>description</descr>
<lines>
<line>12345</line>
</lines>
</phone>
如果您发现自己处于特殊情况,需要保留一些具有某些属性设置的空字段。您所要做的就是稍微更改traverse_and_clean
方法:
def traverse_and_clean(kid)
kid.children.map { |child| traverse_and_clean(child) }
kid.remove if kid.content.blank? && kid.attributes.blank?
end
答案 2 :(得分:1)
require 'nokogiri'
doc = Nokogiri::XML::Document.parse <<-EOXML
<phone>
<name>test</name>
<descr>description</descr>
<empty/>
<lines>
<line>12345</line>
<css/>
</lines>
</phone>
EOXML
nodes = doc.xpath("//phone//*[not(text())]")
nodes.each{|n| n.remove if n.elem? }
puts doc
<强>输出强>
<?xml version="1.0"?>
<phone>
<name>test</name>
<descr>description</descr>
<lines>
<line>12345</line>
</lines>
</phone>
答案 3 :(得分:1)
与@ JustinKo的回答类似,仅使用CSS选择器:
require 'nokogiri'
doc = Nokogiri::XML(<<EOT)
<?xml version="1.0"?>
<phone>
<name>test</name>
<descr>description</descr>
<empty/>
<lines>
<line>12345</line>
<css/>
</lines>
</phone>
EOT
doc.search(':empty').remove
puts doc.to_xml
看看它做了什么:
<?xml version="1.0"?>
<phone>
<name>test</name>
<descr>description</descr>
<lines>
<line>12345</line>
</lines>
</phone>
Nokogiri实现了很多jQuery的选择器,所以总是值得看看这些扩展可以做什么。