2009-07-30 8 views
0

Étant donné un document XML comme celui-ci:Vérification du contenu du nœud contre un ensemble de valeurs

<root> 
    <foo> 
     <bar>a</bar> 
     <bar>b</bar> 
     <bar>c</bar> 
    </foo> 
    ... 
</root> 

Comment puis-je récupérer tous les foo -nodes qui ont bar -subnodes avec certaines valeurs?

Ainsi par exemple, si je dois tous foo -elements qui ont bar -subelements avec des valeurs a et c, je suis actuellement en utilisant cette expression:

//*/foo[bar/text()='a'][bar/text()='c'] 

ce qui est bien, sauf qu'il obtient maladroit si J'ai plus de "bar -constraints" et je ne suis pas trop grand d'un fan d'expressions XPath générées par programme :). Ce que je cherche est quelque chose le long de ces lignes (syntaxe évidemment invalide):

//*/foo[bar/text() in-set('a', 'c')] 

Des idées?

Répondre

1

Il n'est pas tout à fait clair si vous voulez ET ou OU. Votre exemple XPath avec deux filtres est un ET (c'est-à-dire que foo a à la fois "a" et "c"), mais les commentaires aux autres réponses semblent impliquer que vous voulez réellement un OR (foo avec "a" ou "c" "). Avec XPath 2.0, ce dernier serait très facile:

//foo[bar[. = ('a', 'c')]] 

et est un peu plus compliqué:

//foo[count(distinct-values(data(bar[. = ('a', 'c')])))) = 2] 

ou, si vous utilisez des variables (je vais vous montrer la syntaxe XQuery, mais dans la pratique, vous devrait utiliser votre API de mise en œuvre de XPath pour fournir des valeurs pour les variables externes):

let $values := ('a', 'c') 
return //foo[count(distinct-values(data(bar[. = $values])))) = count($values)] 
1

Vous pouvez utiliser

//*/foo[bar/text()='a' or bar/text()='c'] 

Il n'y a pas 'dans' opérateur XPath. Voici un list of xpath operators.

+0

Bon, mais ça ne m'aide pas vraiment. Je devrais encore créer par programmation cette séquence qui, avec suffisamment de contraintes, peut être assez longue. Au lieu de cela, je préfèrerais simplement créer la chaîne ''a '', 'b', 'c' et ne pas toucher l'expression XPath elle-même. – n3rd

0

Si vous avez accès à XPath 2.0, vous pouvez utiliser le matches function:

//foo[fn:matches(bar, "a|c")] 

Malheureusement XPath 2.0 est pas largement pris en charge.

réponse de Tomalak m'a donné une idée:

//foo[bar[fn:matches(text(),"a|c")]] 

Cela pourrait fonctionner, car il ne fonctionne pas sur un ensemble de nœuds, mais un nœud texte individuel.

+0

J'ai juste essayé votre suggestion, mais il semble que 'matches()' n'accepte pas une liste comme premier paramètre. – n3rd

+0

Merde. Figures La ligne de fond est XPath n'est pas bon à ce genre de chose. Il n'y a pas de moyen simple de faire ce que vous voulez sans utiliser plusieurs clauses '[]' ou des conditions 'ou'. – Welbog

+0

La seconde expression correspondra à tous les noeuds 'foo' qui ont un noeud' bar' avec soit '" a "' ou '" c "'. Ce qui est nécessaire est de ne faire correspondre que les nœuds 'foo' qui ont _both_' bar' avec '" a "', et un autre 'bar' avec' "c" '. –

0

Pour les expressions "ET" vous êtes plus ou moins coincé avec ce que vous avez actuellement. Bien que je l'écris comme:

//*/foo[bar/text()='a' and bar/text()='c']

Pour "ou" expressions (votre question ne sont pas claires à ce sujet), vous pouvez essayer avec:

//*/foo[bar[contains(',a,c,', concat(',', text(), ','))]]

Ou, plus lisiblement écrit:

 
//*/foo[ 
    bar[ 
    contains(
     ',a,c,', 
     concat(',', text(), ',') 
    ) 
    ] 
] 

Ceci trouve <foo> éléments qui contiennent <bar> éléments qui se sont conformes à une certaine règle. Et la règle est la suivante: "la valeur du texte (entre guillemets) est contenue dans la chaîne de recherche (entre guillemets)".

Pour une valeur de texte de 'a' et une chaîne de recherche de 'a,b', vous rechercheriez ',a,' dans ',a,b,'. Donc, si l'un des éléments internes <bar> correspond, l'élément externe <foo> va être sélectionné.

Vous devez choisir un délimiteur qui ne peut pas être contenu dans les valeurs, évidemment. À partir de votre exemple de code, j'ai choisi ',' mais tout caractère valide fera l'affaire.

+0

Cela ne nécessiterait-il pas que les éléments 'bar' soient dans un ordre spécifique et que je sache cet ordre à l'avance? – n3rd

+0

Non. Voir ma réponse élargie. – Tomalak

0

Vous pouvez être sournoise à ce sujet et ajouter un élément à votre document comme:

<values> 
    <value>a</value> 
    <value>c</value> 
</values> 

Alors ce XPath

/root/foo[count(bar[.=/root/values/value]) = count(/root/values/value)] 

trouverez tous les éléments foo où le nombre de bar enfants dont la valeur est dans la liste des valeurs est égal au nombre de valeurs dans cette liste. Cela fonctionnera - si tous les éléments bar contiennent des valeurs distinctes.

Questions connexes