似乎这个问题之前没有在stackoverflow上讨论过,除了使用嵌套XPath谓词…改进了不涉及嵌套谓词的解决方案。
所以我试着写出我想要得到的过于简化的示例:
输入:<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竟然没有这个明显的特性。
XPath 2.0一行程序:
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解决方案中,将使用扩展函数进行比较,并使用xxx:node-set()
扩展来测试通过在变量体中应用模板产生的RTF是否包含任何子元素):
<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>
当将此转换应用于所提供的XML文档(更正为格式良好)时:
<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>
生成所需的正确结果:
<root>
<hungryAnimals>
<cage name="B"/>
<cage name="D"/>
</hungryAnimals>
</root>
说明:请注意XSLT current()
函数的使用
XPath 1.0不是"相对完整的"——它不能进行任意连接。如果使用XSLT,总是可以通过将变量绑定到中间节点集,或者(有时)使用current()函数来绕过这些限制。
XPath 2.0引入了范围变量,这使得它相对完整,所以这个限制已经消除了。
<xsl:when test="cage[@animal = /root/shortOfSupply/food/@animal]">
还不够表达你们的测试情况吗?
注意
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>