使用 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 工件。如果您在代码块上使用足够的前导空格并为块引用使用足够的>
,则可以使“块引用内的代码块”工作。我想我修好了,今天早上我感觉有点强迫症
抱歉回复晚了。 @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”对象时如何避免创建无意义的空白文本节点