Создание XML-документа с корневым элементом с пространством имен с помощью Nokogiri builder
Я реализую экспортер для формата данных XML, для которого требуются пространства имен. Я использую Nokogiri XML Builder (версия 1.4.0) для этого.
Однако я не могу заставить Nokogiri создать root node с пространством имен.
Это работает:
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">
Пространства имен должны быть определены перед использованием, по-видимому, поэтому нет возможности добавить его в корень node.
Я нашел вопрос об этом в списке рассылки Nokogiri, но у него не было ответов. У кого-нибудь есть решение?
Ответы
Ответ 1
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']
до того, как будет определено пространство имен, то есть перед тем, как передать его в качестве аргумента в корневой каталог node. Таким образом, приведенный выше код добавляет пространство имен после факта к корню node.
Ответ 2
Два года спустя я нашел более чистый способ сделать это, используя ссылку на Nokogiri::XML::Builder
для получения корня node и добавить к нему пространство имен. Как и предыдущее решение, для этого требуется, чтобы root node существовал до того, как пространства имён могут быть добавлены к нему.
Я изменил элемент <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>
Мне нравится это лучше, потому что вам не нужно искать пространство имен, и проще добавить несколько пространств имен.
Ответ 3
Спустя три года после ответа Люка это стало еще проще. Теперь вы можете использовать пространство имен "до", которое оно определило. Этот код:
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>