2010-01-29 2 views
34

Voici un extrait de mon xml:XPath: sélectionnez tous les frères et sœurs suivants jusqu'à ce qu'un autre frère

<node/> 
<node/> 
<node id="1">content</node> 
<node/> 
<node/> 
<node/> 
<node id="2">content</node> 
<node/> 
<node/> 

Je suis positionné dans le node[@id='1']. J'ai besoin d'un Xpath pour faire correspondre tous les éléments <node/> jusqu'au prochain nœud non vide (ici node[@id='2']).


Edit: les attributs ne sont à @ ID expliquer mon problème plus clairement, mais ne sont pas dans mon XML d'origine. J'ai besoin d'une solution qui n'utilise pas les attributs @id.


Je ne pas veulent faire correspondre les frères et sœurs vides après node[@id='2'], donc je ne peux pas utiliser un following-sibling::node[text()=''] naïf.

Comment puis-je y parvenir?

+0

Voir aussi: http://stackoverflow.com/questions/2165566/xslt-select-following-sibling -until-reach-a-specified-tag – WBT

+0

Cela peut être utile: [http://stackoverflow.com/questions/2063619/how-to-reformat-xml-with-group-adjacent-xslt](http:/ /stackoverflow.com/questions/2063619/how-to-reformat-xml-with-group-adjacent-xslt) – igor

Répondre

21

Vous pourrait le faire de cette façon:

 
../node[not(text()) and preceding-sibling::node[@id][1][@id='1']] 

'1' est l'identifiant du noeud courant (générer l'expression dynamique).

L'expression dit:

  • du contexte actuel aller au parent
  • sélectionner les nœuds enfants qui
  • ont pas de texte et
  • de tous les « noeuds précédents frères et soeurs qui ont une carte d'identité "le premier doit avoir un identifiant de 1

Si vous êtes en XSLT, vous pouvez sélectionner l'un des axes suivants ause vous pouvez utiliser la fonction current():

<!-- the for-each is merely to switch the current node --> 
<xsl:for-each select="node[@id='1']"> 
    <xsl:copy-of select=" 
    following-sibling::node[ 
     not(text()) and 
     generate-id(preceding-sibling::node[@id][1]) 
     = 
     generate-id(current()) 
    ] 
    " /> 
</xsl:for-each> 

ou plus simple (et plus efficace) avec une clé:

<xsl:key 
    name="kNode" 
    match="node[not(text())]" 
    use="generate-id(preceding-sibling::node[@id][1])" 
/> 

<xsl:copy-of select="key('kNode', generate-id(node[@id='1']))" /> 
+0

Je suis finalement allé une autre route, parce que je suis en dehors de XSLT, donc je sélectionne tous les noeuds suivants, et itérer sur eux, et arrêter ma boucle quand je rencontre le prochain pas vide. J'accepte votre réponse comme la plus complète, parce que je crois maintenant qu'il n'y a pas de one-liner dans XPath pour faire ce que j'ai demandé. – glmxndr

+0

@subifié: Erm - mais il y a * un XPath one-liner qui fait ça dans ma réponse ?! – Tomalak

+0

oui, vous avez raison, ma question n'est pas très claire sur le fait que les attributs id que j'ai mis étaient seulement pour montrer et expliquer le problème. Je n'ai pas les attributs id dans mon XML. – glmxndr

8

XPath 2.0 a les opérateurs '< <' et '>>' où node1 << node2 est true si node1 précède node2 dans l'ordre du document. donc basée sur cela avec XPath 2.0 dans un XSLT 2.0 stylesheet où le noeud courant est le nœud [@id = « 1 »] vous pouvez utiliser

following-sibling::node[not(text()) and . << current()/following-sibling::node[@od][1]] 

qui doit également la fonction actuelle() de XSLT, donc C'est pourquoi j'ai dit "avec XPath 2.0 dans une feuille de style XSLT 2.0". La syntaxe ci-dessus est pure XPath, dans une feuille de style XSLT, vous devez vous échapper '< <' as '& lt; & lt; '.

8

que la réponse Simpler acceptée:

//node[@id='1']/following-sibling::node[following::node[@id='2']] 
  • Trouver un nœud partout dont l'id est '1'
  • Maintenant, trouver tous les frères et soeurs node éléments suivants
  • ... mais seulement si les les éléments ont également un node avec id="2" quelque part après eux.

montré en action avec un plus clair document de test (et id juridiques valeurs):

xml = '<root> 
<node id="a"/><node id="b"/> 
<node id="c">content</node> 
<node id="d"/><node id="e"/><node id="f"/> 
<node id="g">content</node> 
<node id="h"/><node id="i"/> 
</root>' 

# A Ruby library that uses libxml2; http://nokogiri.org 
require 'nokogiri'; doc = Nokogiri::XML(xml) 

expression = "//node[@id='c']/following-sibling::node[following::node[@id='g']]" 
puts doc.xpath(expression) 
#=> <node id="d"/> 
#=> <node id="e"/> 
#=> <node id="f"/> 
+0

Pourquoi avez-vous choisi l'axe 'following' ici? Cela peut donner des faux positifs plus loin sur la route, si le document est plus grand (en plus de l'axe suivant étant plus lent que tout autre axe). – Abel

Questions connexes