使用Nokogiri的XML Builder添加命名空间

时间:2011-11-04 13:23:36

标签: ruby xml nokogiri xml-namespaces

我一直在破坏我的头几个小时,但我似乎无法确定如何在使用Nokogiri XML Builder类构建XML结构时添加XMLNS名称空间。

例如,考虑下面的XML示例:我可以在GetQuote标记之间创建所有内容,但创建“p:ACMRequest”仍然是一个谜。

我遇到了这个引用https://gist.github.com/428455/7a15f84cc08c05b73fcec2af49947d458ae3b96a,这对我来说仍然没有意义。即使参考XML文档,http://www.w3.org/TR/xml-names/,也没有多大意义。

<?xml version="1.0" encoding="UTF-8"?>
<p:ACMRequest xmlns:p="http://www.acme.com" xmlns:p1="http://www.acme.com/datatypes" xmlns:p2="http://www.acme.com/ACMRequestdatatypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.acme.com ACM-req.xsd ">
  <GetQuote>
    <Request>
      <ServiceHeader>
        ...
        ...
      </ServiceHeader>
    </Request>
    <From>
      ...
      ...
    </From>
    <Details>
      ...
      ...
    </Details>
  </GetQuote>
</p:ACMRequest>

2 个答案:

答案 0 :(得分:14)

如果您使用Google "nokogiri xml builder namespace",那么首次点击是Nokogiri documentation page,其中包含:

  

与属性类似地添加命名空间。 Nokogiri::XML::Builder假设当属性以“xmlns”开头时,它应该是命名空间:

        
   builder = Nokogiri::XML::Builder.new { |xml|
     xml.root('xmlns' => 'default', 'xmlns:foo' => 'bar') do
       xml.tenderlove
     end
   }
   puts builder.to_xml
     

将输出这样的XML:

        
   <?xml version="1.0"?>
   <root xmlns:foo="bar" xmlns="default">
     <tenderlove/>
   </root>

将此问题应用于您的具体问题,只需执行以下操作:

require 'nokogiri'
NS = {
  "xmlns:p"   => "http://www.acme.com",
  "xmlns:p1"  => "http://www.acme.com/datatypes",
  "xmlns:p2"  => "http://www.acme.com/ACMRequestdatatypes",
  "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
}
builder = Nokogiri::XML::Builder.new { |xml|
  xml.ACMRequest(NS) do
    xml.GetQuote
  end
}
puts builder.to_xml

#=> <?xml version="1.0"?>
#=> <ACMRequest xmlns:p="http://www.acme.com" xmlns:p1="http://www.acme.com/datatypes" xmlns:p2="http://www.acme.com/ACMRequestdatatypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
#=>   <GetQuote/>
#=> </ACMRequest>

至于根元素本身的名称空间前缀......

<p:ACMRequest xmlns:p="…">…</p:ACMRequest>

...我无法弄清楚如何在创建过程中将名称空间前缀应用于Nokogiri中的第一个元素。相反,您必须在创建文档后应用命名空间:

root = builder.doc.root
acme = root.namespace_definitions.find{ |ns| ns.href==NS["xmlns:p"] }
root.namespace = acme
puts builder.to_xml

#=> <?xml version="1.0"?>
#=> <p:ACMRequest xmlns:p="http://www.acme.com" xmlns:p1="http://www.acme.com/datatypes" xmlns:p2="http://www.acme.com/ACMRequestdatatypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">atypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
#=>   <GetQuote/>
#=> </p:ACMRequest>

或者,你可以作弊:

# This happens to work for now, but I doubt you should rely upon it.
builder.doc.root.name = "p:ACMRequest"

每个“Creating an XML document with a namespaced root element with Nokogiri builder”您可以通过小型黑客在创建过程中执行此操作:

builder = Nokogiri::XML::Builder.new { |xml|
  xml.ACMRequest(NS) do
   xml.parent.namespace = … # find the ns in xml.parent.namespace_definitions
   # …
  end
end

答案 1 :(得分:12)

require 'nokogiri'
NS = {
  "xmlns:p"   => "http://www.acme.com",
  "xmlns:p1"  => "http://www.acme.com/datatypes",
  "xmlns:p2"  => "http://www.acme.com/ACMRequestdatatypes",
  "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
}
builder = Nokogiri::XML::Builder.new { |xml|
  xml['p'].ACMRequest(NS) do
    xml.GetQuote
  end
}
puts builder.to_xml

产地:

<?xml version="1.0"?>
<p:ACMRequest xmlns:p="http://www.acme.com" xmlns:p1="http://www.acme.com/datatypes" xmlns:p2="http://www.acme.com/ACMRequestdatatypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <p:GetQuote/>
</p:ACMRequest>

构建器API中记录了这一点:http://nokogiri.org/Nokogiri/XML/Builder.html

  

引用声明的名称空间

     

引用非默认命名空间(即标签“foo:bar”)的标签可以   使用Nokogiri :: XML :: Builder#[]方法构建。