75 votes

Convertir un document Nokogiri en Rubh Hash

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

Quelque chose comme Rails ' Hash.from_xml .

108voto

Si vous souhaitez convertir un document XML Nokogiri en un hachage, procédez comme suit:

 require 'active_support/core_ext/hash/conversions'
hash = Hash.from_xml(nokogiri_document.to_s)
 

22voto

Phrogz Points 112337

Voici une version beaucoup plus simple qui crée un hachage robuste incluant des informations sur les espaces de noms, aussi bien pour les éléments que pour 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=>"!"
#=>         }
#=>       ]
#=>     }
#=>   ]
#=> }
 

14voto

A.Ali Points 411

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 à consulter ROXML ( http://github.com/Empact/roxml/tree ), qui mappe les éléments xml en objets ruby; il 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
 

14voto

John Hinnegan Points 2784

J'ai trouvé cela tout en essayant tout simplement de convertir XML de Hachage (pas dans les Rails). Je pensais que j'allais utiliser Nokogiri, mais il a fini par aller avec Nori.

Mon code était trival:

response_hash = Nori.parse(response)

Mise à JOUR: d'autres utilisateurs ont souligné que cela ne fonctionne pas. Je n'ai pas vérifié, mais il me semble que la méthode d'analyse a été déplacé à partir de la classe de l'instance. Mon code ci-dessus a travaillé à un certain point. Nouveau (non vérifié) le code serait:

response_hash = Nori.new.parse(response)

4voto

Si vous définissez quelque chose comme ceci dans votre configuration:

 ActiveSupport::XmlMini.backend = 'Nokogiri'
 

il comprend un module dans Nokogiri et vous obtenez la méthode to_hash .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X