向Nokogiri::XML::Builder文档中添加XML元素

9
我该如何将一个 Nokogiri::XML::Element 添加到使用 Nokogiri::XML::Buider 创建的 XML 文档中?
我的目前解决方案是序列化该元素并使用 << 方法让 Builder 重新解释它。
orig_doc = Nokogiri::XML('<root xmlns="foobar"><a>test</a></root>')
node = orig_doc.at('/*/*[1]')

puts Nokogiri::XML::Builder.new do |doc|
    doc.another {
        # FIXME: this is the round-trip I would like to avoid
        xml_text = node.to_xml(:skip_instruct => true).to_s
        doc << xml_text

        doc.second("hi")
    }
end.to_xml

# The expected result is
#
# <another>
#    <a xmlns="foobar">test</a>
#    <second>hi</second>
# </another>

然而Nokogiri::XML::Element是一个相当大的节点(在千字节和数千个节点的级别),而这段代码位于热路径中。分析显示序列化/解析往返非常昂贵。

我该如何指示Nokogiri Builder将现有的XML元素node添加到“当前”位置?

2个回答

11

不使用私有方法,您可以使用Builder实例的parent方法获取当前父元素的句柄。然后,您可以将一个元素附加到该元素中(即使是来自另一个文档)。例如:

require 'nokogiri'
doc1 = Nokogiri.XML('<r><a>success!</a></r>')
a = doc1.at('a')

# note that `xml` is not a Nokogiri::XML::Document,
#  but rather a Nokogiri::XML::Builder instance.
doc2 = Nokogiri::XML::Builder.new do |xml|
  xml.some do
    xml.more do
      xml.parent << a
    end
  end
end.doc

puts doc2
#=> <?xml version="1.0"?>
#=> <some>
#=>   <more>
#=>     <a>success!</a>
#=>   </more>
#=> </some>

这比我用 #insert 搞的要好得多。 - gioele
当我尝试这个时,我丢失了一个XML属性的命名空间。 - Surya

2

在查看了Nokogiri源代码后,我找到了这个脆弱的解决方案:使用受保护的#insert(node)方法。

修改后的代码如下所示:

doc.another {
    xml_text = node.to_xml(:skip_instruct => true).to_s
    doc.send('insert', xml_text) # <= use `#insert` instead of `<<`

    doc.second("hi")
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接