Existe-t-il un moyen facile de convertir un document XML Nokogiri en un hachage?
Quelque chose comme Rails 'Hash.from_xml
.
J'utilise ce code avec libxml-Ruby (1.1.3). Je n'ai pas utilisé nokogiri moi-même, mais je comprends qu'il utilise quand même libxml-Ruby. Je vous encourage également à regarder ROXML ( http://github.com/Empact/roxml/tree ) qui mappe les éléments xml aux objets Ruby; c'est construit sur libxml.
# USAGE: Hash.from_libxml(YOUR_XML_STRING)
require 'xml/libxml'
# adapted from
# http://movesonrails.com/articles/2008/02/25/libxml-for-active-resource-2-0
class Hash
class << self
def from_libxml(xml, strict=true)
begin
XML.default_load_external_dtd = false
XML.default_pedantic_parser = strict
result = XML::Parser.string(xml).parse
return { result.root.name.to_s => xml_node_to_hash(result.root)}
rescue Exception => e
# raise your custom exception here
end
end
def xml_node_to_hash(node)
# If we are at the root of the document, start the hash
if node.element?
if node.children?
result_hash = {}
node.each_child do |child|
result = xml_node_to_hash(child)
if child.name == "text"
if !child.next? and !child.prev?
return result
end
elsif result_hash[child.name.to_sym]
if result_hash[child.name.to_sym].is_a?(Object::Array)
result_hash[child.name.to_sym] << result
else
result_hash[child.name.to_sym] = [result_hash[child.name.to_sym]] << result
end
else
result_hash[child.name.to_sym] = result
end
end
return result_hash
else
return nil
end
else
return node.content.to_s
end
end
end
end
Si vous souhaitez convertir un document XML Nokogiri en hachage, procédez comme suit:
require 'active_support/core_ext/hash/conversions'
hash = Hash.from_xml(nokogiri_document.to_s)
Voici une version beaucoup plus simple qui crée un hachage robuste qui inclut des informations d'espace de noms, à la fois pour les éléments et les attributs:
require 'nokogiri'
class Nokogiri::XML::Node
TYPENAMES = {1=>'element',2=>'attribute',3=>'text',4=>'cdata',8=>'comment'}
def to_hash
{kind:TYPENAMES[node_type],name:name}.tap do |h|
h.merge! nshref:namespace.href, nsprefix:namespace.prefix if namespace
h.merge! text:text
h.merge! attr:attribute_nodes.map(&:to_hash) if element?
h.merge! kids:children.map(&:to_hash) if element?
end
end
end
class Nokogiri::XML::Document
def to_hash; root.to_hash; end
end
Vu en action:
xml = '<r a="b" xmlns:z="foo"><z:a>Hello <b z:m="n" x="y">World</b>!</z:a></r>'
doc = Nokogiri::XML(xml)
p doc.to_hash
#=> {
#=> :kind=>"element",
#=> :name=>"r",
#=> :text=>"Hello World!",
#=> :attr=>[
#=> {
#=> :kind=>"attribute",
#=> :name=>"a",
#=> :text=>"b"
#=> }
#=> ],
#=> :kids=>[
#=> {
#=> :kind=>"element",
#=> :name=>"a",
#=> :nshref=>"foo",
#=> :nsprefix=>"z",
#=> :text=>"Hello World!",
#=> :attr=>[],
#=> :kids=>[
#=> {
#=> :kind=>"text",
#=> :name=>"text",
#=> :text=>"Hello "
#=> },
#=> {
#=> :kind=>"element",
#=> :name=>"b",
#=> :text=>"World",
#=> :attr=>[
#=> {
#=> :kind=>"attribute",
#=> :name=>"m",
#=> :nshref=>"foo",
#=> :nsprefix=>"z",
#=> :text=>"n"
#=> },
#=> {
#=> :kind=>"attribute",
#=> :name=>"x",
#=> :text=>"y"
#=> }
#=> ],
#=> :kids=>[
#=> {
#=> :kind=>"text",
#=> :name=>"text",
#=> :text=>"World"
#=> }
#=> ]
#=> },
#=> {
#=> :kind=>"text",
#=> :name=>"text",
#=> :text=>"!"
#=> }
#=> ]
#=> }
#=> ]
#=> }
J'ai trouvé cela en essayant de convertir simplement XML en Hash (pas dans Rails). Je pensais que j'utiliserais Nokogiri, mais j'ai fini par choisir Nori .
Ensuite, mon code était trival:
response_hash = Nori.parse(response)
D'autres utilisateurs ont souligné que cela ne fonctionne pas. Je n'ai pas vérifié, mais il semble que la méthode d'analyse a été déplacée de la classe vers l'instance. Mon code ci-dessus a fonctionné à un moment donné. Le nouveau code (non vérifié) serait:
response_hash = Nori.new.parse(response)
Utilisez Nokogiri pour analyser la réponse XML à Ruby hachage. C'est assez rapide.
doc = Nokogiri::XML(response_body)
Hash.from_xml(doc.to_s)
Si vous définissez quelque chose comme ça dans votre configuration:
ActiveSupport::XmlMini.backend = 'Nokogiri'
il comprend un module dans Nokogiri et vous gagnez le to_hash
méthode.
Si le nœud que vous avez sélectionné dans Nokogiri se compose d'une seule balise, vous pouvez extraire les clés, les valeurs et les compresser en un seul hachage, comme ceci:
@doc ||= Nokogiri::XML(File.read("myxmldoc.xml"))
@node = @doc.at('#uniqueID') # this works if this selects only one node
nodeHash = Hash[*@node.keys().Zip(@node.values()).flatten]
Voir http://www.Ruby-forum.com/topic/125944 pour plus d'informations sur Ruby fusion de tableaux.