【问题标题】:Cleaning XML document recursively from empty tags with Nokogiri?使用 Nokogiri 从空标签递归地清理 XML 文档?
【发布时间】:2015-02-18 11:20:37
【问题描述】:

我有一个如下所示的嵌套 XML 文档:

<?xml version="1.0"?>
<phone>
  <name>test</name>
  <descr>description</descr>
  <empty/>
  <lines>
    <line>12345</line>
    <css/>
  </lines>
</phone>

我需要删除所有空的 XML 节点,例如 &lt;empty/&gt;&lt;css/&gt;

我最终得到了类似的东西:

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

上面的代码只删除了第一个空标签,例如&lt;empty/&gt;。我无法进入嵌套块。我想我需要一些递归策略。我仔细阅读了 Nokogiri 文档并检查了很多示例,但我还没有找到解决方案。

我该如何解决这个问题?

我正在使用 Ruby 1.9.3 和 Nokogiri 1.5.10。

【问题讨论】:

    标签: ruby xml recursion nokogiri


    【解决方案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
    
    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】:

    您应该能够使用 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..好的!! Nokogiri::XML::Nodeset 也有 remove 方法,我不知道!
    • 需要注意的是,这似乎改变了节点集。我最初并没有指望它会侵入我的 xml。不过效果很好! :) remove 调用的返回是被拉出的标签... FWIW
    【解决方案3】:
    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>
    

    【讨论】:

    • 我不知道如何删除那些空白行。如果有人知道诀窍,请告诉我! :(
    • 如果xml片段完全放在一行上,您知道为什么您的解决方案不起作用吗?像这样:code testdescription12345电话>
    【解决方案4】:

    类似于@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 的选择器,所以看看这些扩展可以做什么总是值得的。

    【讨论】:

      猜你喜欢
      • 2014-01-08
      • 1970-01-01
      • 2018-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-03
      • 1970-01-01
      • 2012-09-13
      相关资源
      最近更新 更多