Создание XML-документа с корневым элементом с пространством имен с помощью Nokogiri builder
-
11-09-2019 - |
Вопрос
Я реализую экспортер для формата данных 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">
Пространства имен, по-видимому, должны быть определены перед использованием, поэтому нет способа добавить их к корневому узлу.
Я нашел вопрос по этому поводу в списке рассылки Nokogiri, но ответов на него не было.У кого-нибудь есть решение?
Решение
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']
до определения пространства имен, то есть до того, как вы передадите его в качестве аргумента корневому узлу.Таким образом, приведенный выше код добавляет пространство имен постфактум к корневому узлу.
Другие советы
Два года спустя я нашел более чистый способ сделать это, используя 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>
Мне это нравится больше, потому что вам не нужно искать пространство имен, и проще добавить несколько пространств имен.
Через три года после ответа Люка все стало еще проще.Теперь вы можете использовать пространство имен "до" его определения.Этот код:
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>