web-dev-qa-db-fra.com

Convertir un document Nokogiri en un Ruby Hash

Existe-t-il un moyen facile de convertir un document XML Nokogiri en un hachage?

Quelque chose comme Rails 'Hash.from_xml.

66
Ivan

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
14
A.Ali

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)
100
Guillaume Roderick

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=>"!"
#=>         }
#=>       ]
#=>     }
#=>   ]
#=> }
16
Phrogz

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)
12
John Hinnegan

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)
9
PythonDev

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.

3
Pierre Schambacher

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.

0
juanfe