3

所以我正在尝试构建一个 XML 文件,它必须如何构建的性质意味着我必须在自然嵌套结构之外添加元素。例如:

builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
  xml.Data {
    xml.Groups do |inner|
      inner.send(:"GroupType", "test")
    end
    # Insert child element into Groups element.
  }
end

我希望 XML 看起来像:

<Data>
  <Groups>
    <GroupType>test</GroupType>
    <AnotherNode>13</AnotherNode>
  </Groups>
</Data>

第一个代码示例中<AnotherNode>添加我的注释的位置。

应该相对简单,但我一生都无法弄清楚。大概我需要能够搜索该块,或者在我形成它然后使用它时引用它?

顺便说一句,我已经接管了一个项目,里面已经有一堆 Nokogiri 的东西,它有如下选择器:

xml_file = Nokogiri::XML(xml)

(xml_file/:RootNode).each do |root|
  (root/:SomeItem).each do |si|
    ...
  end
end

..但我在文档中找不到类似的东西?这是怎么回事?

4

4 回答 4

3

仅使用 Nokogiri::XML::Builder,您需要这样做:

require 'nokogiri'

builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
  xml.Data {
    xml.Groups {
      xml.GroupType "test"
      xml.AnotherNode "13"
    }
  }
end

puts builder.to_xml
Which outputs:

=> <?xml version="1.0" encoding="UTF-8"?>
   <Data>
     <Groups>
       <GroupType>test</GroupType>
       <AnotherNode>13</AnotherNode>
     </Groups>
   </Data>

Builder 是一种 DSL,其设计目的是为了方便,具有有限的功能集。如果您不想以“Builder-way”的方式进行操作,您可以使用“old-school”并采用现有的 XML 节点,并在其基础上进行构建:

require 'nokogiri'

builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
  xml.Data {
    xml.Groups {
      xml.GroupType "test"
    }
  }
end

这在 Builder 对象中创建了所需的基本 XML。将其渲染为 XML 并将其重新解析为 Nokogiri::XML::Document,然后对其进行处理:

doc = Nokogiri::XML(builder.to_xml)
doc.at('GroupType').add_next_sibling("<AnotherNode>13</AnotherNode>")
puts doc.to_xml

=> <?xml version="1.0" encoding="UTF-8"?>
   <Data>
     <Groups>
       <GroupType>test</GroupType><AnotherNode>13</AnotherNode>
     </Groups>
   </Data>

doc = Nokogiri::XML(builder.to_xml)
doc.at('Groups').add_child("<AnotherNode>13</AnotherNode>")
puts doc.to_xml

=> <?xml version="1.0" encoding="UTF-8"?>
   <Data>
     <Groups>
       <GroupType>test</GroupType>
     <AnotherNode>13</AnotherNode></Groups>
   </Data>

上述两种方式中的任何一种都在语法上呈现相同的事物,它们只是在外观上有所不同。

您甚至可以通过以下方式变得复杂和时髦:

builder = Nokogiri::XML::Builder.with(
  Nokogiri::XML(
    builder.to_xml
  ).at('Groups') << "<AnotherNode>13</AnotherNode>"
)
puts builder.to_xml

=> <?xml version="1.0" encoding="UTF-8"?>
   <Data>
     <Groups>
       <GroupType>test</GroupType>
     <AnotherNode>13</AnotherNode></Groups>
   </Data>
于 2012-12-03T11:50:55.050 回答
3

在事实发生两年后遇到了这个问题,并认为接受的答案比它需要的要复杂得多。

问题是,您在这里构建一个 XML 片段,而默认情况下 Nokogiri 的构建器假定您正在构建一个完整的文档。XML 文档只有一个根节点。

一种选择是按如下方式构建完整的文档,然后获取根的孩子:

<DISPOSABLE_OUTER_NODE>
  <Data>
    <Groups>
      <GroupType>test</GroupType>
     </Groups>
  </Data>
  <AnotherNode>13</AnotherNode>
</DISPOSABLE_OUTER_NODE>

但是有一种更好的“碎片化”方式。您构建一个空片段,然后让构建器处理它而不是一个新文档:

frag = Nokogiri::XML::DocumentFragment.parse("")

Nokogiri::XML::Builder.with( frag ){ |b|
  b.Data do
    b.Groups do
      b.GroupType "test"
    end
  end

  b.AnotherNode "13"
}

puts frag.to_xml

然后将其粘贴到您正在修改的任何文档中。

也许with是 Nokogiri 的后来者。但这是解决我遇到的问题的优雅方法,所以我认为它属于这里。

于 2015-03-23T19:59:49.870 回答
2

我有同样的问题。

我猜以下代码可以满足您的需求(或多或少)。

builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
  xml.Data {
    p = nil
    xml.Groups do |inner|
      inner.send(:"GroupType", "test") do |n|
        p = n.parent
      end
    end
    # Insert child element into Groups element.
    xb = Nokogiri::XML::Builder.new({}, p)
    xb.someOtherChild
  }
end
于 2013-01-29T15:56:00.933 回答
1

我想做类似的事情,但在我的情况下,在创建初始“构建器”对象之后添加了额外的 XML 节点。基于“碎片化”答案,我发现 Builder 对象有一个 doc 属性,允许通过 Builder.with() 直接访问 XML 中的任何节点。下面的解决方案将允许传递“构建器”对象,以便函数/对象根据需要添加 XML 数据。

builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
  xml.Data {
    xml.Groups do |inner|
      inner.send(:"GroupType", "test")
    end  
  }
end
Nokogiri::XML::Builder.with(builder.doc.at('Data/Groups')) do |xml|
  xml.AnotherNode_ 13
end
于 2018-11-06T00:10:05.060 回答