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

Posted

技术标签:

【中文标题】使用 Nokogiri 的 XML Builder 添加命名空间【英文标题】:Adding namespace using Nokogiri's XML Builder 【发布时间】:2022-01-18 18:22:16 【问题描述】:

我已经苦恼了几个小时,但我似乎无法确定如何在使用 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>

【问题讨论】:

【参考方案1】:

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"

根据“How to create 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

【讨论】:

@muistooshort 不;前四个块(报价、代码、报价、代码)都来自页面。为了清楚起见,我会将它们全部缩进。 这看起来像是一个编辑错误,但它只是一个 Markdown 工件。如果您在代码块上使用足够的前导空格并为块引用使用足够的&gt;,则可以使“块引用内的代码块”工作。我想我修好了,今天早上我感觉有点强迫症 抱歉回复晚了。 @Phrogz 感谢您的广泛回复。我在 XML 方面的经验相当有限,但您的回答也解决了我的其他一些问题。我已经记不清我看过多少次 Nokogiri 文档了,但直到其他人解释它才有意义。 :-) 是的,我也注意到代码块中的错字:-) 恭喜,现在谷歌上的第一个点击是this page。【参考方案2】:
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#[] 方法构建。

【讨论】:

警告:这会在所有嵌套标签中添加命名空间 (p)。

以上是关于使用 Nokogiri 的 XML Builder 添加命名空间的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Nokogiri Builder 创建具有命名空间根元素的 XML 文档

创建“Nokogiri::XML”或“Nokogiri::HTML”对象时如何避免创建无意义的空白文本节点

如何使用Nokogiri解析带有非对标签的XML

使用Savon和Nokogiri在Rails中解析XML SOAP响应的内存不足

如何使用Nokogiri从XML中删除元素

Nokogiri XML XPath 不选择 OpenXML 关系文件中的节点