使用Nokogiri构建器创建带有命名空间根元素的XML文档

时间:2009-12-01 22:35:26

标签: xml ruby namespaces nokogiri

我正在为需要命名空间的XML数据格式实现导出器。我正在使用Nokogiri XML Builder(版本1.4.0)来执行此操作。

但是,我无法让Nokogiri创建一个带有命名空间的根节点。

这有效:

Nokogiri::XML::Builder.new { |xml| xml.root('xmlns:foobar' => 'my-ns-url') }.to_xml

<?xml version="1.0"?>
<root xmlns:foobar="my-ns-url"/>

就像这样:

Nokogiri::XML::Builder.new do |xml| 
  xml.root('xmlns:foobar' => 'my-ns-url') { xml['foobar'].child }
end.to_xml

<?xml version="1.0"?>
<root xmlns:foobar="my-ns-url">
  <foobar:child/>
</root>

但是,我需要类似<foo:root>的内容,但这不起作用:

Nokogiri::XML::Builder.new { |xml| xml['foobar'].root('xmlns:foobar' => 'my-ns-url') }.to_xml

NoMethodError: undefined method `namespace_definitions' for #<Nokogiri::XML::Document:0x11bfef8 name="document">

显然,在使用之前必须定义命名空间,因此无法将其添加到根节点。

我找到a question about this on the Nokogiri mailing list,但没有回复。有人有解决方案吗?

3 个答案:

答案 0 :(得分:15)

require 'rubygems'
require 'nokogiri'

puts Nokogiri::XML::Builder.new { |xml| 
  xml.root("xmlns:foo"=>"url") {
    xml.parent.namespace = xml.parent.namespace_definitions.find{|ns|ns.prefix=="foo"}
    xml['foo'].child
  }
}.to_xml

在定义命名空间之前,即在将其作为参数传递给根节点之前,不能使用xml['foo']。因此,上面的代码将事后名称空间添加到根节点。

答案 1 :(得分:7)

两年后,我发现了一种更简洁的方法,通过使用Nokogiri::XML::Builder的文档引用来检索根节点并将命名空间添加到该节点。与之前的解决方案一样,它需要在将名称空间添加到根节点之前存在根节点。

我已将<root>元素更改为<rootElement>,因此更清楚:

builder = Nokogiri::XML::Builder.new do |xml| 
  xml.rootElement do
    # create a namespace and save it for later
    ns = xml.doc.root.add_namespace_definition('foo', 'my-ns-url')
    # you can also create multiple name spaces
    xml.doc.root.add_namespace_definition('bar', 'http://example.com/bar')

    # assign the saved namespace to rootElement
    xml.doc.root.namespace = ns

    xml['foo'].child
    xml['bar'].child
  end
end

现在,builder.to_xml将返回:

<?xml version="1.0"?>
<foo:rootElement xmlns:foo="my-ns-url" xmlns:bar="http://example.com/bar">
  <foo:child/>
  <bar:child/>
</foo:rootElement>

我更喜欢这个,因为您不必搜索名称空间,并且添加多个名称空间更容易。

答案 2 :(得分:2)

卢克回答三年之后,它变得更加简单了。您现在可以在定义它之前使用命名空间。这段代码:

require 'nokogiri'

NAMESPACES = { 'xmlns:foo' => 'bar', 'xmlns:baz' => 'bat' }

builder = Nokogiri::XML::Builder.new { |xml|
  xml['foo'].RootElement(NAMESPACES) {
    xml['baz'].FirstChild
  }
}

puts builder.to_xml

输出此XML:

<?xml version="1.0"?>
<foo:RootElement xmlns:foo="bar" xmlns:baz="bat">
  <baz:FirstChild/>
</foo:RootElement>