XPath / XSLT嵌套谓词:如何获取外部谓词的上下文?

时间:2011-07-06 10:36:32

标签: xslt xpath nested predicate

似乎这个问题之前没有在stackoverflow上讨论过,除了Working With Nested XPath Predicates ... Refined之外,还提供了不涉及嵌套谓词的解决方案。

所以我试着写出我想要得到的过分简化的样本:

输入:

<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>

输出:

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

或者,如果没有交叉点,

<root>
    <everythingIsFine/>
</root>

我希望使用嵌套谓词来获取它:

<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>

那我该怎么写呢?????

我知道我可以使用一个模板和变量/参数的大量使用来重写整个样式表,但它甚至使这个样式表变得更加复杂,更不用说真实问题的真实样式表了。

在XPath参考中写道,点.符号表示当前上下文节点,但它没有告诉在此之前是否有可能获取上下文节点;我无法相信XPath缺少这个明显的功能。

4 个答案:

答案 0 :(得分:10)

XPath 2.0 one-liner

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

当应用于提供的XML文档时,选择

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

不能使用单个XPath 1.0表达式来查找给定的笼子包含饥饿的动物。

这是一个XSLT解决方案(XSLT 2.0仅用于避免使用扩展函数进行比较 - 在​​XSLT 1.0解决方案中,将使用扩展函数进行比较和{{ 1}}扩展来测试通过在变量体中应用模板生成的RTF是否包含任何子元素):

xxx:node-set()

将此转换应用于提供的XML文档(更正为格式良好):

<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>

产生了想要的正确结果

<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>

解释:请注意使用XSLT <root> <hungryAnimals> <cage name="B"/> <cage name="D"/> </hungryAnimals> </root> 函数。

答案 1 :(得分:4)

XPath 1.0不是“关系完整” - 它不能进行任意连接。如果你在XSLT中,你总是可以通过将变量绑定到中间节点集来解决限制,或者(有时)使用current()函数。

XPath 2.0引入了范围变量,这使它在关系上完整,因此这种限制已经消失。

答案 2 :(得分:0)

<xsl:when test="cage[@animal = /root/shortOfSupply/food/@animal]">不足以表达您的测试条件吗?

答案 3 :(得分:0)

注意 XPath 中的点运算符与当前上下文相关。在 XSLT 中,当前模板上下文 _由函数current()提供,其中大部分时间(并非总是)与.


您可以使用父轴缩写(../)执行测试(以及应用模板):

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

此外,第一个模板中的匹配模式是错误的,它应该是相对于根:

 /root/animalsDictionary

@Martin建议也显然是正确的。

您的最终模板略有修改:

<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>