2010-12-13 45 views
0

Voici mon problème. J'appelle un service qui renvoie plusieurs nœuds identiques qui contiennent des valeurs différentes. J'ai besoin d'obtenir les valeurs GUID de ces noeuds et les stocker comme une variable dans mon script pour les utiliser plus tard.Ruby XML: donne des nœuds uniques XML existants à lire

Exemple de XML que j'écris du service:

<ShippingMethod> 
    <Description>Description Goes Here</Description> 
    <HandlingCharge>16.98</HandlingCharge> 
    <ShippingMethodId>GUID</ShippingMethodId> 
    <ShippingMethodName>Express Overnight</ShippingMethodName> 
    </ShippingMethod> 
<ShippingMethod> 
    <Description>Description2 Goes Here</Description> 
    <HandlingCharge>19.98</HandlingCharge> 
    <ShippingMethodId>GUID2</ShippingMethodId> 
    <ShippingMethodName>More Express Overnight</ShippingMethodName> 
    </ShippingMethod> 

J'ai plusieurs de ces par demande et ils sont dynamiques. Je ne veux pas couper ça en utilisant regex basé sur les valeurs que j'ai actuellement. C'est hacky et me mordra plus tard. La seule chose qui m'intéresse à ce stade est de lire ce XML et d'extraire toutes les valeurs par requête et de les mettre dans un tableau que je peux mapper dans mon code. Ma question est la suivante: si vous aviez ce morceau de XML et que vous deviez obtenir GUID et GUID2 pour être stockés en tant que variables dans un script Ruby, que suggéreriez-vous d'utiliser pour l'analyser et avez-vous un exemple de lecture et décapage? valeur?

ROXML, REXML, Nokogiri, Regex ???

J'apprécie votre aide!
~ Cordialement Uninspired

+0

Ne pas utiliser regex à moins que le XML est totalement sous votre contrôle et est simple. Un analyseur comme Nokogiri est très simple à utiliser et est beaucoup plus robuste. Imaginez ce qui se passerait si votre XML passait d'un format joli-empreint/indenté à tout sur une seule ligne, et que vous utilisiez regex. Un analyseur ne s'en soucierait pas. –

Répondre

4

Certainement, utilisez une bibliothèque d'analyse XML! la seule raison que je pourrais penser pour le faire manuellement est d'éviter une dépendance de gemme. Quant à la bibliothèque, c'est très subjectif, mais je recommande Nokogiri: rapide, concis et puissant.

require 'nokogiri' 
doc = Nokogiri::XML.parse(xml_string) 
doc.css("ShippingMethod ShippingMethodId").map(&:text) # ["GUID", "GUID2"] 
+0

Cela a fonctionné! Merci!! – r3nrut

2

J'ai utilisé REXML pour des tâches comme celle-ci avec succès dans le passé. Le script suivant va extraire les valeurs GUID:

require "rexml/document" 

doc = REXML::Document.new File.open('doc.xml') 
guids = doc.root.get_elements('//ShippingMethodId').map { |element| element.get_text } 

en supposant un nom de fichier « doc.xml » ou vous pouvez simplement passer dans la chaîne XML au lieu d'un fichier. Vous devrez envelopper le fragment XML en un seul élément racine pour le rendre XML bien formé avant REXML va l'analyser:

<Root> 
    <ShippingMethod> 
    <Description>Description Goes Here</Description> 
    <HandlingCharge>16.98</HandlingCharge> 
    <ShippingMethodId>GUID</ShippingMethodId> 
    <ShippingMethodName>Express Overnight</ShippingMethodName> 
    </ShippingMethod> 
    <ShippingMethod> 
    <Description>Description2 Goes Here</Description> 
    <HandlingCharge>19.98</HandlingCharge> 
    <ShippingMethodId>GUID2</ShippingMethodId> 
    <ShippingMethodName>More Express Overnight</ShippingMethodName> 
    </ShippingMethod> 
</Root> 
2

Voici quelques façons que je le ferais:

require 'nokogiri' 

xml = '<xml><ShippingMethod> 
    <Description>Description Goes Here</Description> 
    <HandlingCharge>16.98</HandlingCharge> 
    <ShippingMethodId>GUID</ShippingMethodId> 
    <ShippingMethodName>Express Overnight</ShippingMethodName> 
</ShippingMethod> 
<ShippingMethod> 
    <Description>Description2 Goes Here</Description> 
    <HandlingCharge>19.98</HandlingCharge> 
    <ShippingMethodId>GUID2</ShippingMethodId> 
    <ShippingMethodName>More Express Overnight</ShippingMethodName> 
</ShippingMethod></xml> 
' 
doc = Nokogiri::XML(xml) 
doc.css('ShippingMethodId').inject([]){ |m,a| m << a.text } # => ["GUID", "GUID2"] 
(doc/'//ShippingMethodId').map{ |n| n.text }    # => ["GUID", "GUID2"] 
doc.search('//ShippingMethodId').map(&:text)    # => ["GUID", "GUID2"] 
doc.search('//ShippingMethodId/text()').map{ |n| n.text } # => ["GUID", "GUID2"] 
doc.search('//ShippingMethodId/text()').map(&:to_s)   # => ["GUID", "GUID2"] 
+0

pourquoi utilisez-vous injecter dans le premier exemple au lieu de la carte? – tokland

+0

C'est juste une manière différente de l'accomplir. Comme ils disent, "Il y a plus d'une façon de peler un chat." –