2016-04-08 4 views
0

Ich möchte mehrere gleich formatierte XML-Dateien in einer CSV-Datei analysieren.So verwenden Sie Nokogiri zum Kombinieren mehrerer gleich formatierter XML-Dateien in eine CSV-Datei

Ich suchte auf Google, nokogiri.org und auf SO, aber ich konnte keine Antwort finden.

Ich habe zehn XML-Dateien im identischen Format in Bezug auf Node/Element-Struktur, die im aktuellen Verzeichnis befinden.

Nach der Kombination der XML-Dateien in eine einzige XML-Datei, muss ich bestimmte Elemente des advisory-Knotens herausziehen. Ich möchte die link, title, location, os -> language -> name und reference -> name Daten in die CSV-Datei ausgeben.

Mein Code ist nur in der Lage ein einzelnes XML-Dokument zu analysieren, und ich würde es in 1 Rechnung tragen mag: viele:

# Parse the XML file into a Nokogiri::XML::Document object 
@doc = Nokogiri::XML(File.open("file.xml")) 

# Gather the 5 specific XML elements out of the 'advisory' top-level node 
data = @doc.search('advisory').map { |adv| 
    [ 
    adv.at('link').content, 
    adv.at('title').content, 
    adv.at('location').content, 
    adv.at('os > language > name').content, 
    adv.at('reference > name').content 
    ] 
} 

# Loop through each array element in the object and write out as CSV row 
CSV.open('output_file.csv', 'wb') do |csv| 
    # Explicitly set headers until you figure out how to get them programatically 
    csv << ['Link', 'Title', 'Location', 'OS Name', 'Reference Name'] 
    data.each do |row| 
    csv << row 
    end 
end 

Ich habe versucht, den Code zu ändern mehrere XML-Dateien zu unterstützen und erhalten sie in Nokogiri :: XML :: Document-Objekte:

xml_docs = [] 

Dir.glob("*.xml").each do |file| 
    xml = Nokogiri::XML(File.new(file)) 
    xml_docs << Nokogiri::XML::Document.new(xml) 
end 

Dies schafft erfolgreich einen Array xml_docs mit den richtigen Objekten, die es in, aber ich weiß nicht, wie diese sechs Objekte zu einem einzigen Objekt zu konvertieren.

Dies ist Beispiel XML. Alle XML-Dateien verwenden den gleichen Knoten/Elementstruktur:

<advisories> 
    <title> Not relevant </title> 
    <customer> N/A </customer> 
    <advisory id="12345"> 
    <link> https://www.google.com </link> 
    <release_date>2016-04-07</release_date> 
    <title> The Short Description Would Go Here </title> 
    <location> Location Name Here </location> 
    <os> 
     <product> 
     <id>98765</id> 
     <name>Product Name</name> 
     </product> 
     <language> 
     <id>123</id> 
     <name>en</name> 
     </language> 
    </os> 
    <reference> 
     <id>00029</id> 
     <name>Full</name> 
     <area>Not Defined</area> 
    </reference> 
    </advisory> 
    <advisory id="98765"> 
    <link> https://www.msn.com </link> 
    <release_date>2016-04-08</release_date> 
    <title> The Short Description Would Go Here </title> 
    <location> Location Name Here </location> 
    <os> 
     <product> 
     <id>12654</id> 
     <name>Product Name</name> 
     </product> 
     <language> 
     <id>126</id> 
     <name>fr</name> 
     </language> 
    </os> 
    <reference> 
     <id>00052</id> 
     <name>Partial</name> 
     <area>Defined</area> 
    </reference> 
    </advisory> 
</advisories> 

Der Code nutzt Nokogiri :: XML :: Dokument aber wenn Nokogiri :: XML :: Builder besser für dies funktioniert, ich bin mehr als bereit zu justieren Mein Code entsprechend.

+0

Willkommen bei Stack Overflow. Während es nett ist, bist du hier umgezogen, leider hast du den Punkt von SO verpasst; Wir helfen * Ihnen * Fehler/Probleme in * Ihrem * Code zu beheben. Bitte lesen Sie "[ask]" einschließlich der Links unten und "[mcve]". Wir würden gerne Beweise für Ihre Bemühungen sehen: Was haben Sie versucht? Warum hat es nicht funktioniert? Wenn du es nicht versucht hast, wo hast du gesucht und warum haben diese Orte nicht die Informationen, die du brauchst? Wenn Sie uns XML geben und uns mitteilen, was Sie tun möchten, riechen Sie, dass wir den Code schreiben sollen, um das Problem zu lösen, anstatt uns zu fragen, wie Sie ein Problem lösen können, dem Sie beim Schreiben begegnet sind. –

+0

Ich werde meine Frage in Kürze mit dem, was ich versucht habe, ändern. Ich wollte die Frage nicht zu lange stellen, aber ich werde sehen, was ich tun kann. Danke für den Hinweis! –

+0

Gern geschehen. SO hat ein ehrgeiziges Ziel, eine Online-Referenz für Programmierprobleme zu sein, eine Art Kochbuch für Probleme und Lösungen. Ihnen geht es gut, wie die Top-Ergebnisse in den Suchmaschinen zeigen, aber es ist eine ständige Aufgabe, dafür zu sorgen, dass die Qualität der Fragen und Antworten hoch bleibt, weshalb wir die Dinge wollen, die wir tun. Lange Fragen bedeuten keine hohe Qualität, so dass sie schwer zu schreiben sind und Voraussicht und Anstrengung erfordern, aber das Endergebnis ist großartig; Sie bekommen eine Antwort und andere auch in Zukunft. Willkommen im Kampf um die Lösung aller Probleme! :-) –

Antwort

0

würde ich den ersten Teil behandeln, von einer XML-Datei parsen, wie folgt aus:

require 'nokogiri' 

doc = Nokogiri::XML(<<EOT) 
<advisories> 
    <advisory id="12345"> 
    <link> https://www.google.com </link> 
    <title> The Short Description Would Go Here </title> 
    <location> Location Name Here </location> 
    <os> 
     <language> 
     <name>en</name> 
     </language> 
    </os> 
    <reference> 
     <name>Full</name> 
    </reference> 
    </advisory> 
    <advisory id="98765"> 
    <link> https://www.msn.com </link> 
    <release_date>2016-04-08</release_date> 
    <title> The Short Description Would Go Here </title> 
    <location> Location Name Here </location> 
    <os> 
     <language> 
     <name>fr</name> 
     </language> 
    </os> 
    <reference> 
     <name>Partial</name> 
    </reference> 
    </advisory> 
</advisories> 
EOT 

Hinweis: Dies hat entfernt Knoten, weil sie auf die Frage nicht wichtig waren. Bitte entfernen Sie Flusen, wenn Sie Fragen stellen, da es ablenkt.

Damit der Kern des Codes zu sein:

doc.search('advisory').map{ |advisory| 
    link = advisory.at('link').text 
    title = advisory.at('title').text 
    location = advisory.at('location').text 
    os_language_name = advisory.at('os > language > name').text 
    reference_name = advisory.at('reference > name').text 

    { 
    link: link, 
    title: title, 
    location: location, 
    os_language_name: os_language_name, 
    reference_name: reference_name 
    } 
} 

Das DRY'd werden konnte, sondern wurde als ein Beispiel dafür, was geschrieben zu tun.

Rennen, dass die Ergebnisse in einem Array von Hashes, die leicht Ausgabe über CSV würde:

# => [ 
     {:link=>" https://www.google.com ", :title=>" The Short Description Would Go Here ", :location=>" Location Name Here ", :os_language_name=>"en", :reference_name=>"Full"}, 
     {:link=>" https://www.msn.com ", :title=>" The Short Description Would Go Here ", :location=>" Location Name Here ", :os_language_name=>"fr", :reference_name=>"Partial"} 
    ] 

Sobald du hast, dass es dann in einer modifizierten Version von Loops zur Ausgabe von CSV passen zu arbeiten und lesen die XML-Dateien. Dies ist nicht getestet, aber sieht ungefähr richtig:

CSV.open('output_file.csv', 'w', 
    headers: ['Link', 'Title', 'Location', 'OS Name', 'Reference Name'], 
    write_headers: true 
) do |csv| 
    Dir.glob("*.xml").each do |file| 
    xml = Nokogiri::XML(File.read(file)) 
    # parse a file and get the array of hashes 
    end 

    # pass the array of hashes to CSV for output 
end 

Beachten Sie, dass Sie eine Datei-Modus von 'wb' verwendet haben. Sie brauchen selten b mit CSV, da CSV ein Textformat sein soll. Wenn Sie sicher sind, werden Sie auf binäre Daten stoßen, dann verwenden Sie 'b' auch, aber das könnte einen Pfad führen, der Drachen enthält.

Beachten Sie auch, dass dies read verwendet.read ist nicht skalierbar, was bedeutet, dass es egal ist, wie groß eine Datei ist, sie wird versuchen, sie in den Speicher einzulesen, unabhängig davon, ob sie tatsächlich passt oder nicht. Es gibt viele Gründe, das zu vermeiden, aber das Beste ist, dass es Ihr Programm in die Knie zwingen wird. Wenn Ihre XML-Dateien den verfügbaren freien Speicher für Ihr System überschreiten könnten, sollten Sie einen SAX-Parser schreiben, den Nokogiri unterstützt. Wie das geht, ist eine andere Frage.


es war tatsächlich ein Array von Arrays von Hashes. Ich bin mir nicht sicher, wie ich dort gelandet, aber ich war einfach array.flatten

Meditieren auf diese nutzen können:

foo = [] # => [] 
foo += [{}] # => [{}] 
:

foo = [] # => [] 
foo << [{}] # => [[{}]] 
foo.flatten # => [{}] 

Sie wahrscheinlich, dies tun wollte

Jedes Mal, wenn ich flatten verwenden muss ich schauen, ob ich das Array erstellen kann, ohne dass es ein Array von Arrays von etwas ist. Es ist nicht so, dass sie von Natur aus schlecht sind, weil sie manchmal sehr nützlich sind, aber du wolltest wirklich eine Reihe von Hashes, also wusstest du, dass etwas nicht in Ordnung ist und dass es mehr CPU-Zeit kostet. Es ist besser, das Problem zu lösen und es zu beheben und mit schnellerem/effizienterem Code zu enden. (Und einige werden sagen, dass dies eine verschwendete Anstrengung oder eine vorzeitige Optimierung ist, aber das Schreiben von effizientem Code ist ein sehr gutes Merkmal und Ziel.)

+0

"Ein Pfad mit Drachen" ... lol. Ich werde das bald versuchen. Danke für die ausführliche Erklärung! –

+0

Danke, das funktioniert gut. Ich habe jedem xml.search eine Variable zugewiesen und diese in ein neues Array geschrieben. Sobald ich mein Array mit 6 Objekten hatte, war es tatsächlich ein Array von Arrays von Hashes. Ich bin mir nicht sicher, wie ich dort gelandet bin, aber ich war einfach in der Lage, 'array.flatten' zu verwenden und über die Hashes zu iterieren, wobei die Hashwerte der CSV zugewiesen wurden. Soll ich meine vollständige Antwort posten? Übrigens, ich habe deinen Namen schon oft gesehen - ich bin froh, dass ich die Chance habe, dir zu danken.Du hast mir und vielen anderen Menschen geholfen - danke! –

+0

Wenn Sie ein Array von Arrays von Hashes hatten, haben Sie wahrscheinlich nicht korrekt verkettet. Ich werde eine Erklärung hinzufügen. Fügen Sie Ihre vollständige Antwort nur dann ein, wenn Sie das Update gefunden haben. Das Posten deines Codes hilft anderen nicht sehr. Mein Ziel ist es, etwas zurückzugeben. Ich habe dieses Zeug schon lange gemacht und hatte viele gute Mentoren. Ich genieße es zu unterrichten, zu betreuen und zu bezahlen. –