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

发布于 2024-11-19 02:14:07 字数 2108 浏览 10 评论 0原文

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

所以我尝试编写我想要得到的过于简单的示例:

输入:

<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 缺少这个明显的功能。

It seems that this question was not discussed on stackoverflow before, save for Working With Nested XPath Predicates ... Refined where the solution not involving nested predicates was offered.

So I tried to write the oversimplified sample of what I'd like to get:

Input:

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

Output:

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

or, alternatively, if there is no intersections,

<root>
    <everythingIsFine/>
</root>

And i want to get it using a nested predicates:

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

So what should i write in place of that ??????

I know i could rewrite the entire stylesheet using one more template and extensive usage of variables/params, but it makes even this stylesheet significantly more complex, let alone the real stylesheet i have for real problem.

It is written in XPath reference that the dot . sign means the current context node, but it doesn't tell whether there is any possibility to get the node of context before that; and i just can't believe XPath is missing this obvious feature.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

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 文档时< /strong>(已更正为格式正确):

<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 2.0 one-liner:

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

When applied on the provided XML document selects:

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

One cannot use a single XPath 1.0 expression to find that a given cage contains a hungry animal.

Here is an XSLT solution (XSLT 2.0 is used only to avoid using an extension function for the comparison -- in an XSLT 1.0 solution one will use an extension function for the comparison and the xxx:node-set() extension to test if the RTF produced by applying templates in the body of the variable contains any child element):

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

When this transformation is applied on the provided XML document (corrected to be well-formed):

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

the wanted, correct result is produced:

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

Explanation: Do note the use of the XSLT current() function.

叹梦 2024-11-26 02:14:07

XPath 1.0 不是“相对完整的”——它不能进行任意连接。如果您使用 XSLT,则始终可以通过将变量绑定到中间节点集或(有时)使用 current() 函数来绕过限制。

XPath 2.0 引入了范围变量,这使得它在关系上变得完整,所以这个限制已经消失了。

XPath 1.0 is not "relationally complete" - it can't do arbitrary joins. If you're in XSLT, you can always get round the limitations by binding variables to intermediate nodesets, or (sometimes) by using the current() function.

XPath 2.0 introduces range variables, which makes it relationally complete, so this limitation has gone.

决绝 2024-11-26 02:14:07

不足以表达您的测试条件吗?

Doesn't <xsl:when test="cage[@animal = /root/shortOfSupply/food/@animal]"> suffice to express your test condition?

臻嫒无言 2024-11-26 02:14:07

注意 XPath 中的点运算符与当前上下文有关。在XSLT中,当前模板上下文由函数current()给出,大多数时候(并不总是) strong>) 与 . 一致。


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

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

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

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

Notice The dot operator in XPath is related to the current context. In XSLT the current template context_ is given by the function current(), which most of the time (not always) coincides with the ..


You can perform the test (and the apply templates as well), using the parent axis abbreviation (../):

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

Moreover the match pattern in the the first template is wrong, it should be relative to the root:

 /root/animalsDictionary

@Martin suggestion is also obviously correct.

Your final template slightly modified:

<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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文