Создание документа XML с корневым элементом в пространстве имен с помощью компоновщика Nokogiri - PullRequest
13 голосов
/ 02 декабря 2009

Я реализую экспортер для формата данных XML, который требует пространства имен. Я использую Nokogiri XML Builder (версия 1.4.0) для этого.

Однако я не могу заставить Нокогири создать корневой узел с пространством имен.

Это работает:

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 , но он не получил ответов. У кого-нибудь есть решение?

Ответы [ 3 ]

15 голосов
/ 06 декабря 2009
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'] до того, как пространство имен будет определено, т.е. до того, как вы передадите его в качестве аргумента корневому узлу. Таким образом, приведенный выше код добавляет пространство имен после факта к корневому узлу.

7 голосов
/ 28 марта 2012

Два года спустя я нашел более чистый способ сделать это, используя ссылку на документ 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 голосов
/ 01 декабря 2015

Через три года после ответа Люка все стало еще проще. Теперь вы можете использовать пространство имен «до», оно определено. Этот код:

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>
...