2016-12-26 1 views
1

Comment utiliser concat() dans une expression xpath de Nokogiri? J'ai essayé:Nokogiri et concat()

xml.xpath("concat(/root/a/text(), /root/b/text())") 

Mais qui ne réussit pas avec:

ArgumentError: node_set must be a Nokogiri::XML::NodeSet

En fait, il semble que Nokogiri accepte seulement des expressions XPath qui commencent par "" ou «/», alors j'ai essayé ces:

xml.xpath(".concat(/root/a/text(), /root/b/text())") 
xml.xpath("/concat(/root/a/text(), /root/b/text())") 
xml.xpath("/concat('foo', 'bar')") 

Mais ils échouent tous avec cette erreur:

Nokogiri::XML::XPath::SyntaxError: Invalid expression: /concat('foo', 'bar')

Je sais Nokogiri est basé sur libxml2 et en tant que tel, il n'applique que XPath 1.0. Mais concat() fait partie de XPath 1.0. Cependant, une fonction similaire dans XPath 2.0 est une chaîne-join() et je lui ai donné une chance:

xml.xpath("string-join('foo', 'bar')")  

Message d'erreur:

RuntimeError: xmlXPathCompOpEval: function string-join not found

Voir, il y a un message d'erreur différent de celui de concat(). Donc, au moins, la fonction concat() est trouvée. Un autre indice qu'il pourrait fonctionner est en quelque sorte ...

xml.xpath("concat()")               

... renvoie le message d'erreur attendu:

Nokogiri::XML::XPath::SyntaxError: Invalid number of arguments: concat()

Toute chance d'obtenir concat() travaillant avec des arguments?

P.S .: Les expressions xpath de base comme xml.xpath("/root/a/text()") fonctionnent très bien.

+1

Pouvez-vous donner un exemple complet de votre problème? Le XPath avec 'concat' fonctionne bien pour moi, je suppose que vous utilisez le résultat quelque part où Nokogiri s'attend à quelque chose d'autre. – matt

Répondre

2

Une requête XPath renvoie généralement un ensemble de noeuds , une collection de noeuds du document. Dans Nokogiri, ceci est représenté par l'objet Nokogiri::XML::NodeSet. Nokogiri vous permet également d'utiliser un NodeSet avec la méthode xpath. Dans ce cas, Nokogiri executes the query individually against each of the nodes in the NodeSet in turn, and combines them all into a new NodeSet which it returns as the result.

Normalement, cela fonctionne comme prévu, avec pour résultat la combinaison de tous les nœuds correspondant à la requête. Toutefois, cela ne fonctionne pas lorsque vous utilisez une requête XPath qui renvoie autre chose qu'un ensemble de nœuds, comme dans ce cas. Après l'exécution de la requête, Nokogiri tente d'ajouter le résultat NodeSet au NodeSet nouvellement créé. Lorsque le résultat est en fait une chaîne this fails with the error you are getting.

Ceci a été élevé dans un Nokogiri bug, et ajouté à leur roadmap, mais il n'y a pas encore de résolution.

La solution de contournement/solution consiste à faire en sorte que toutes vos requêtes XPath renvoyées non définies par un nœud soient exécutées sur des nœuds uniques plutôt que sur des ensembles de nœuds. at_xpath retournera le premier nœud correspondant pour une requête, et ce nœud peut être utilisé pour d'autres appels xpath sans ce problème.

1

semblons travailler pour moi

body = "<root><a>aaa</a><b>bbb</b></root>" 
xml = Nokogiri::XML(body) 
xml.xpath("concat(/root/a/text(), /root/b/text())") # => "aaabbb" 

Compte tenu de votre mise à jour, peut-être essayer cela?

body = "<wrapper><root><a>aaa</a><b>bbb</b></root></wrapper>" 
xml = Nokogiri::XML(body) 
xml = xml.xpath("wrapper") # returns a node set 
xml.map { |each| each.xpath("concat(./root/a/text(), ./root/b/text())") } # => ["aaabbb"] 

NB, notez le premier . pour ancrer la requête au niveau du noeud courant.