Создание XML-документа с корневым элементом с пространством имен с помощью Nokogiri builder

StackOverflow https://stackoverflow.com/questions/1829425

  •  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>
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top