Prédicats imbriqués de XPath/XSLT: comment obtenir le contexte du prédicat externe?

il semble que cette question n'a pas été discutée sur stackoverflow avant, sauf pour Travailler Avec Des Prédicats XPath Imbriqués ... Raffinée où la solution n'impliquant pas de prédicats imbriqués a été offerte.

alors j'ai essayé d'écrire l'échantillon trop simplifié de ce que j'aimerais obtenir:

Entrée:

<root>
    <shortOfSupply>
        <food animal="doggie"/>
        <food animal="horse"/>
    </shortOfSupply>
    <animalsDictionary>
        <cage name="A" animal="kittie"/>
        <cage name="B" animal="dog"/>
        <cage name="C" animal="cow"/>
        <cage name="D" animal="zebra"/>
    </animals>
</root>

Sortie:

<root>
    <hungryAnimals>
        <cage name="B"/>
        <cage name="D"/>
    </hungryAnimals>
</root>

ou, à défaut, si il n'y a pas d'intersections,

<root>
    <everythingIsFine/>
</root>

Et je veux la faire à l'aide d'un prédicats emboîtés:

<xsl:template match="cage">
    <cage>
        <xsl:attribute name="name">
            <xsl:value-of select="@name"/>
        </xsl:attribute>
    </cage>
</xsl:template>

<xsl:template match="/root/animalsDictionary">
    <xsl:choose>
        <!--                                                             in <food>     in <cage>       -->
        <xsl:when test="cage[/root/shortOfSupply/food[ext:isEqualAnimals(./@animal, ?????/@animal)]]">
            <hungryAnimals>
                <xsl:apply-templates select="cage[/root/shortOfSupply/food[ext:isEqualAnimals(@animal, ?????/@animal)]]"/>
            </hungryAnimals>
        </xsl:when>
        <xsl:otherwise>
            <everythingIsFine/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

alors qu'est-ce que je devrais écrire à la place de ça <!--4?

je sais que je pourrais réécrire toute la feuille de style en utilisant un modèle de plus et l'utilisation étendue des variables / params, mais il rend même cette feuille de style beaucoup plus complexe, sans parler de la vraie feuille de style que j'ai pour vrai problème.

Il est écrit dans XPath de référence que la dot . signe signifie le noeud de contexte courant, mais il ne dit pas s'il y a possibilité d'obtenir le noeud de contexte avant cela; et je ne peux pas croire que XPath manque cette caractéristique évidente.

12
demandé sur Community 2011-07-06 14:36:31

4 réponses

XPath 2.0 one-liner:

for $a in /*/animalsDictionary/cage
      return
        if(/*/shortOfSupply/*[my:isA($a/@animal, @animal)])
          then $a
          else ()

lorsqu'il est appliqué sur le document XML fourni, sélectionne:

   <cage name="B"/>
   <cage name="D"/>

on ne peut pas utiliser une seule expression XPath 1.0 pour trouver qu'une cage donnée contient un animal affamé.

Voici une solution XSLT (XSLT 2.0 est utilisé seulement pour éviter d'utiliser une fonction d'extension pour la comparaison -- dans une solution XSLT 1.0 on utilisera une fonction d'extension pour la la comparaison et l' xxx:node-set() extension pour tester si la RTF produites par l'application de modèles dans le corps de la variable contient aucun élément enfant):

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:my="my:my" exclude-result-prefixes="xs my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <my:Dict>
  <a genName="doggie">
    <name>dog</name>
    <name>bulldog</name>
    <name>puppy</name>
  </a>
  <a genName="horse">
    <name>horse</name>
    <name>zebra</name>
    <name>pony</name>
  </a>
  <a genName="cat">
    <name>kittie</name>
    <name>kitten</name>
  </a>
 </my:Dict>

 <xsl:variable name="vDict" select=
  "document('')/*/my:Dict/a"/>

 <xsl:template match="/">
  <root>
   <xsl:variable name="vhungryCages">
    <xsl:apply-templates select=
    "/*/animalsDictionary/cage"/>
   </xsl:variable>

   <xsl:choose>
    <xsl:when test="$vhungryCages/*">
     <hungryAnimals>
       <xsl:copy-of select="$vhungryCages"/>
     </hungryAnimals>
    </xsl:when>
    <xsl:otherwise>
     <everythingIsFine/>
    </xsl:otherwise>
   </xsl:choose>
  </root>
 </xsl:template>

 <xsl:template match="cage">
  <xsl:if test="
  /*/shortOfSupply/*[my:isA(current()/@animal,@animal)]">

  <cage name="{@name}"/>
  </xsl:if>
 </xsl:template>

 <xsl:function name="my:isA" as="xs:boolean">
  <xsl:param name="pSpecName" as="xs:string"/>
  <xsl:param name="pGenName" as="xs:string"/>

  <xsl:sequence select=
   "$pSpecName = $vDict[@genName = $pGenName]/name"/>
 </xsl:function>
</xsl:stylesheet>

lorsque cette transformation est appliquée sur le document XML fourni (corrigé pour être bien formé):

<root>
    <shortOfSupply>
        <food animal="doggie"/>
        <food animal="horse"/>
    </shortOfSupply>
    <animalsDictionary>
        <cage name="A" animal="kittie"/>
        <cage name="B" animal="dogs"/>
        <cage name="C" animal="cow"/>
        <cage name="D" animal="zebras"/>
    </animalsDictionary>
</root>

le résultat voulu et correct est produit:

<root>
   <hungryAnimals>
      <cage name="B"/>
      <cage name="D"/>
   </hungryAnimals>
</root>

Explication: Ne remarque l'utilisation de XSLT current() fonction.

10
répondu Dimitre Novatchev 2011-07-06 16:03:28

XPath 1.0 n'est pas" complet sur le plan relationnel " - il ne peut pas faire de jointures arbitraires. Si vous êtes dans XSLT, vous pouvez toujours contourner les limitations en liant les variables aux nodesets intermédiaires, ou (parfois) en utilisant la fonction current ().

XPath 2.0 introduit des variables de portée, ce qui le rend complet par rapport, donc cette limitation a disparu.

4
répondu Michael Kay 2011-07-06 15:51:05

Ne pas <xsl:when test="cage[@animal = /root/shortOfSupply/food/@animal]"> suffisant pour exprimer votre condition de test?

0
répondu Martin Honnen 2011-07-06 11:20:48

Avis L'opérateur point dans XPath est liée au contexte actuel. XSLTcontexte du modèle actuel _ est donné par la fonction current(), qui la plupart du temps (toujours) coïncide avec l' ..


vous pouvez effectuer le test (ainsi que les Modèles applicables), en utilisant l'abréviation de l'axe parent (../):

 cage[@animal=../../shortOfSupply/food/@animal]

de plus, le schéma de correspondance en le premier modèle est faux, il doit être relatif à la racine:

 /root/animalsDictionary

la suggestion de@Martin est aussi évidemment correcte.

Votre dernier modèle légèrement modifié:

<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="root/animalsDictionary">
        <xsl:choose>
            <xsl:when test="cage[@animal=../../shortOfSupply/food/@animal]">
                <hungryAnimals>
                    <xsl:apply-templates select="cage[@animal
                            =../../shortOfSupply/food/@animal]"/>
                </hungryAnimals>
            </xsl:when>
            <xsl:otherwise>
                <everythingIsFine/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="cage">
        <cage name="{@name}"/>
    </xsl:template> 

</xsl:stylesheet>
0
répondu Emiliano Poggi 2011-07-06 11:35:41