Tatsächlich ist Rails 'Hash.from_xml ordentlich im MiniXML-Abschnitt des Rails-Codes verpackt. Ich wollte es extrahieren, seit ich es geschrieben habe. Gib mir einen Schubs, wenn du nicht bald davon hörst.
@JellicleCat, ja. Verschwenden Sie keine CPU-Analyse von XML mit Nokogiri, nur damit Nokogiri es in XML ausgibt, um es von etwas anderem zu analysieren. Übergeben Sie einfach das unformatierte XML und fertig.
der Blechmann
Antworten:
14
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-0classHashclass << selfdeffrom_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 hereendenddefxml_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
endelsif 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
endelse
result_hash[child.name.to_sym] = result
endendreturn result_hash
elsereturnnilendelsereturn node.content.to_s
endendendend
Dies sollte die sauberste Antwort sein, +1 auf diesen Vater
Alexis Rabago Carvajal
6
HINWEIS: Das OP ist sich from_xmlder Notwendigkeit ähnlicher Dinge bewusst und erwähnt diese. Verwenden from_xmlbeantwortet 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.
der Blechmann
18
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'classNokogiri::XML::Node
TYPENAMES = {1=>'element',2=>'attribute',3=>'text',4=>'cdata',8=>'comment'}
defto_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?
endendendclassNokogiri::XML::Documentdefto_hash; root.to_hash; endend
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:
Ich denke, dies ist die beste Lösung für Apps, die keine Rails verwenden.
B Seven
Die nicht verifizierte Zeile funktioniert. Wenn Sie jedoch ein Nokogiri::XMLDokument haben, müssen Sie to_szuerst 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 Arrayohne die Feldnamen zurückgegeben zu werden.
Code_Dredd
Nachdem ich meinen Kopf gegen die Wand geschlagen hatte, um Nokogiri zu benutzen, stieß ich schließlich darauf. Das ist bei weitem die beste Lösung! Danke für den Beitrag.
Albert Rannetsperger
11
Verwenden Sie Nokogiri , um die XML-Antwort auf Ruby-Hash zu analysieren. Es ist ziemlich schnell.
doc.to_sgibt zurück, was Sie bereits haben response_body, so dass nokogiri in Ihrem Beispiel nutzlos ist
alesguzik
1
@alesguzik ist im Grunde richtig in dieser Aussage, dass Sie die XML zweimal analysieren Hash.from_xml wird standardmäßig REXML verwenden, nicht Nokogiri auch nicht sicher, ob Sie dies ändern können
Jesse Whitham
2
Nokogiri ist manchmal widerstandsfähiger, schlecht geformte oder codierte XMLs zu analysieren. Ich habe Beispiele, bei denen Hash.from_xml (xml_str) fehlschlagen würde, aber dies würde immer noch funktionieren. So kann es ein Fallback für Hash.from_xml (xml_str) sein
user4887419
Beachten Sie, dass die Hash.from_xmlFunktion nicht verwendet werden sollte, wenn Genauigkeit wichtig ist. Diese Funktion fällt bei komplexeren XML-Dokumenten flach, wobei bestimmte Werte vollständig weggelassen werden.
PyRabbit
3
Wenn Sie in Ihrer Konfiguration so etwas definieren:
ActiveSupport::XmlMini.backend = 'Nokogiri'
Es enthält ein Modul in Nokogiri und Sie erhalten die to_hashMethode.
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]
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
quelle
= 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)
quelle
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. Verwendenfrom_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=>"!" #=> } #=> ] #=> } #=> ] #=> }
quelle
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)
quelle
Nokogiri::XML
Dokument haben, müssen Sieto_s
zuerst dessen Methode aufrufen . ZBxml = Nokogiri::XML(File.open('file.xml'))
und dannhash = Nori.new.parse(xml.to_s)
, aber die Felder scheinen alsArray
ohne die Feldnamen zurückgegeben zu werden.Verwenden Sie Nokogiri , um die XML-Antwort auf Ruby-Hash zu analysieren. Es ist ziemlich schnell.
quelle
doc.to_s
gibt zurück, was Sie bereits habenresponse_body
, so dass nokogiri in Ihrem Beispiel nutzlos istHash.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.quelle
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 .
quelle
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"]}}]
quelle