Gibt es eine einfache Möglichkeit, ein Nokogiri-XML-Dokument in einen Hash zu konvertieren?
So etwas wie Rails Hash.from_xml
.
Gibt es eine einfache Möglichkeit, ein Nokogiri-XML-Dokument in einen Hash zu konvertieren?
So etwas wie Rails Hash.from_xml
.
Hash.from_xml(nokogiri_doc.to_xml)
?
Antworten:
Ich benutze diesen Code mit libxml-ruby (1.1.3). Ich habe selbst kein Nokogiri verwendet, aber ich verstehe, dass es sowieso libxml-ruby verwendet. Ich möchte Sie auch dazu ermutigen, sich ROXML ( http://github.com/Empact/roxml/tree ) anzusehen, das XML-Elemente Ruby-Objekten zuordnet . Es ist auf libxml aufgebaut.
# 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
= strict
zu = false
. Vielen Dank!
Wenn Sie ein Nokogiri-XML-Dokument in einen Hash konvertieren möchten, gehen Sie wie folgt vor:
require 'active_support/core_ext/hash/conversions'
hash = Hash.from_xml(nokogiri_document.to_s)
from_xml
kommt. Es ist keine Standard-Ruby-Methode.
typecast_xml_value(unrename_keys(ActiveSupport::XmlMini.parse(xml)))
from_xml
der Notwendigkeit ähnlicher Dinge bewusst und erwähnt diese. Verwenden from_xml
beantwortet die Frage nicht. Wenn das Dokument bereits ein Nokogiri-Dokument ist, konvertieren Sie es nicht in eine Zeichenfolge, nur um es mit einem anderen XML-Parser zu analysieren. Übergeben Sie stattdessen das unformatierte XML und ignorieren Sie das Parsen mit Nokogiri. Alles andere ist eine Verschwendung von CPU-Zeit.
Hier ist eine weitaus einfachere Version, die einen robusten Hash erstellt, der Namespace-Informationen sowohl für Elemente als auch für Attribute enthält:
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
In Aktion gesehen:
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=>"!"
#=> }
#=> ]
#=> }
#=> ]
#=> }
Ich habe dies beim Versuch gefunden, XML einfach in Hash zu konvertieren (nicht in Rails). Ich dachte, ich würde Nokogiri benutzen, ging aber schließlich zu Nori .
Dann war mein Code trival:
response_hash = Nori.parse(response)
Andere Benutzer haben darauf hingewiesen, dass dies nicht funktioniert. Ich habe nicht überprüft, aber es scheint, dass die Analysemethode von der Klasse in die Instanz verschoben wurde. Mein Code oben hat irgendwann funktioniert. Neuer (nicht verifizierter) Code wäre:
response_hash = Nori.new.parse(response)
Nokogiri::XML
Dokument haben, müssen Sie to_s
zuerst dessen Methode aufrufen . ZB xml = Nokogiri::XML(File.open('file.xml'))
und dann hash = Nori.new.parse(xml.to_s)
, aber die Felder scheinen als Array
ohne die Feldnamen zurückgegeben zu werden.
Verwenden Sie Nokogiri , um die XML-Antwort auf Ruby-Hash zu analysieren. Es ist ziemlich schnell.
doc = Nokogiri::XML(response_body)
Hash.from_xml(doc.to_s)
doc.to_s
gibt zurück, was Sie bereits haben response_body
, so dass nokogiri in Ihrem Beispiel nutzlos ist
Hash.from_xml
Funktion nicht verwendet werden sollte, wenn Genauigkeit wichtig ist. Diese Funktion fällt bei komplexeren XML-Dokumenten flach, wobei bestimmte Werte vollständig weggelassen werden.
Wenn Sie in Ihrer Konfiguration so etwas definieren:
ActiveSupport::XmlMini.backend = 'Nokogiri'
Es enthält ein Modul in Nokogiri und Sie erhalten die to_hash
Methode.
Wenn der in Nokogiri ausgewählte Knoten nur aus einem Tag besteht, können Sie die Schlüssel und Werte extrahieren und wie folgt in einen Hash komprimieren:
@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]
Weitere Informationen zum Zusammenführen von Ruby-Arrays finden Sie unter http://www.ruby-forum.com/topic/125944 .
Schauen Sie sich das einfache Mix-In an, das ich für den Nokogiri XML-Knoten erstellt habe.
http://github.com/kuroir/Nokogiri-to-Hash
Hier ist ein Anwendungsbeispiel:
require 'rubygems'
require 'nokogiri'
require 'nokogiri_to_hash'
html = '
<div id="hello" class="container">
<p>Hello! visit my site <a href="http://kuroir.com">Kuroir.com</a></p>
</div>
'
p Nokogiri.HTML(html).to_hash
=> [{:div=>{:class=>["container"], :children=>[{:p=>{:children=>[{:a=>{:href=>["http://kuroir.com"], :children=>[]}}]}}], :id=>["hello"]}}]