Xslt 为什么没有兄弟轴?

Xslt 为什么没有兄弟轴?,xslt,Xslt,查看XSLT中可用的轴,我必须发现没有同级轴是前同级和后同级的联合。对我来说,这有点令人惊讶,因为我已经写了一个答案(),其中这个轴会很有帮助(尽管到目前为止我只有大约10个答案)。当然,很明显,您可以通过使用union来解决问题。所以这个轴不是必需的。但它偶尔会非常方便,就像所有其他轴一样,它会使代码更可读,更易于维护 有人知道为什么这个轴被遗漏了吗?这可能有一个不明显的原因吗 顺便说一句:我在StackExchange上发现了至少一个问题,其中警告使用前面的同级和后面的同级轴可能会降低性能。

查看XSLT中可用的轴,我必须发现没有
同级
轴是
前同级
后同级
的联合。对我来说,这有点令人惊讶,因为我已经写了一个答案(),其中这个轴会很有帮助(尽管到目前为止我只有大约10个答案)。当然,很明显,您可以通过使用union来解决问题。所以这个轴不是必需的。但它偶尔会非常方便,就像所有其他轴一样,它会使代码更可读,更易于维护

有人知道为什么这个轴被遗漏了吗?这可能有一个不明显的原因吗


顺便说一句:我在StackExchange上发现了至少一个问题,其中警告使用
前面的同级
后面的同级
轴可能会降低性能。但我假设所有包含XML树大部分的轴都是以嵌套方式使用的,这是正确的。因此,遗漏的原因不可能是由于性能。

因为有一段时间没有关于这个问题的活动,我想自己回答。当然,从评论中可以看出,很难追溯到XSLT1.0规范的负责人为什么省略了
同级轴

最具决定性的原因之一可能与@JLRiche和@MichaelKay的评论有关:axis被认为是针对参考节点的特定方向,并且可能很难确定同级节点的方向

为了进一步研究这一点,我设置了一个测试XSLT和一个测试输入XML来检查轴是如何工作的(见下文),特别是轴中节点的顺序。结果令我惊讶:

  • 前面的同级轴
不是从最靠近参考节点的节点开始,而是从最靠近文档开头的节点开始
  • 以下同级的
    确实从引用节点开始
  • 这实际上允许定义

    sibling := preceding-sibling | following-sibling
    
    该集合中的节点从文档开始到结束不断迭代。不会有“跳跃”

    建议的替代方案

    ../node except .
    
    也可以很好地工作,并以相同的顺序生成相同的集合。然而,在看一个不熟悉的XSLT时,我会假设一个
    同级
    轴会比使用父子结构更好地解释逻辑

    有趣的是,轴不是从距离参考节点最近的节点开始,而是从距离文档开头最近的节点开始,这一事实也适用于
    前面的
    祖先的
    ,因此,例如
    安塞斯特::节点[1]
    不会返回节点的父节点,而是返回根节点

    我问这个问题的最初动机是不必重复强加在节点属性上的冗长的
    条件,例如,我不想写

    preceding-sibling::node[CONDITION] | following-sibling::node[CONDITION]
    
    但是,由于上面的表达式可以重写为

    (preceding-sibling::node | following-sibling::node)[CONDITION]
    
    必须使用两个轴而不是同级轴的缺点并不像想象的那么糟糕。当然,在XSLT2.0中,这也适用于

    (../node except .)[CONDITION]
    
    所以,为了回答我的问题:我认为没有一个好的理由不定义
    同级轴。我想没人想到。:-)

    测试设置

    此XML测试输入

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <node id="1">
      <node id="2">
        <node id="3">
          <node id="4"/>
          <node id="5"/>
          <node id="6"/>
        </node>
        <node id="7">
          <node id="8"/>
          <node id="9"/>
          <node id="10"/>
        </node>
        <node id="11">
          <node id="12"/>
          <node id="13"/>
          <node id="14"/>
        </node>
      </node>    
      <node id="15">
        <node id="16">
          <node id="17"/>
          <node id="18"/>
          <node id="19"/>
        </node>
        <node id="20">
          <node id="21"/>
          <node id="22"/>
          <node id="23"/>
        </node>
        <node id="24">
          <node id="25"/>
          <node id="26"/>
          <node id="27"/>
        </node>
      </node>
      <node id="28">
        <node id="29">
          <node id="30"/>
          <node id="31"/>
          <node id="32"/>
        </node>
        <node id="33" value="A">
          <node id="34"/>
          <node id="35"/>
          <node id="36"/>
        </node>
        <node id="37">
          <node id="38"/>
          <node id="39"/>
          <node id="40"/>
        </node>
        <node id="41">
          <node id="42"/>
          <node id="43"/>
          <node id="44"/>
        </node>
        <node id="45" value="A">
          <node id="46"/>
          <node id="47"/>
          <node id="48"/>
        </node>
      </node>
    </node>
    

    我怀疑这个轴的大多数用法可以更简单地表示为
    。/X
    ,例外情况是当前上下文节点本身就是一个X。在XPath 2.0中,这很容易解决-
    。/X除外。
    这正是我想的情况:X是应该忽略的上下文节点。如果我添加轴,我的优先顺序是前面的兄弟姐妹或自我,后面的兄弟姐妹或自我——除了名称太冗长之外,使用
    (后面的兄弟姐妹::*|)更快。
    。为了得到所有的兄弟姐妹,
    。/*
    通常就是为了这个目的。除非最初的工作组有人读到这个问题,或者在某个地方提到没有兄弟姐妹的决定::
    axis,否则我认为你运气不好。但要冒险猜测一下,所有可用的轴都有一个方向:它们向前、向后或无处移动(如
    self::
    )。
    同级::
    轴会同时向两个方向移动,这可能被视为与轴的概念不一致。@JLRishe是:确定同级::x[1]应该是什么肯定很困难!
    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet 
        version="2.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="text" version="1.0" encoding="UTF-8" indent="yes" />
      <xsl:strip-space elements="*"/>
    
      <xsl:variable name="id" select="'37'"/>
    
      <xsl:template name="dump">
        <xsl:text> </xsl:text>
        <xsl:value-of select="@id"/>
      </xsl:template>
    
      <xsl:template match="//node[@id = $id]">
    
        <xsl:text>preceding siblings: </xsl:text>
        <xsl:for-each select="preceding-sibling::node">
          <xsl:call-template name="dump"/>
        </xsl:for-each>
        <xsl:text>&#10;following siblings: </xsl:text>
        <xsl:for-each select="following-sibling::node">
          <xsl:call-template name="dump"/>
        </xsl:for-each>
        <xsl:text>&#10;preceding and following siblings: </xsl:text>
        <xsl:for-each select="preceding-sibling::node | following-sibling::node">
          <xsl:call-template name="dump"/>
        </xsl:for-each>
        <xsl:text>&#10;preceding and following siblings with value A: </xsl:text>
        <xsl:for-each select="(preceding-sibling::node | following-sibling::node)[@value = 'A']">
          <xsl:call-template name="dump"/>
        </xsl:for-each>
        <xsl:text>&#10;following siblings: </xsl:text>
        <xsl:for-each select="following-sibling::node">
          <xsl:call-template name="dump"/>
        </xsl:for-each>
        <xsl:text>&#10;parent's children: </xsl:text>
        <xsl:for-each select="../node">
          <xsl:call-template name="dump"/>
        </xsl:for-each>
        <xsl:text>&#10;parent's children except self: </xsl:text>
        <xsl:for-each select="../node except .">
          <xsl:call-template name="dump"/>
        </xsl:for-each>
        <xsl:text>&#10;parent's children except self with value A: </xsl:text>
        <xsl:for-each select="(../node except .)[@value = 'A']">
          <xsl:call-template name="dump"/>
        </xsl:for-each>
        <xsl:text>&#10;ancestors: </xsl:text>
        <xsl:for-each select="ancestor::node">
          <xsl:call-template name="dump"/>
        </xsl:for-each>
        <xsl:text>&#10;immediate ancestor: </xsl:text>
        <xsl:for-each select="(ancestor::node)[1]"> 
          <xsl:call-template name="dump"/>
        </xsl:for-each>
        <xsl:text>&#10;ancestors or self: </xsl:text>
        <xsl:for-each select="ancestor-or-self::node">
          <xsl:call-template name="dump"/>
        </xsl:for-each>
        <xsl:text>&#10;descendants: </xsl:text>
        <xsl:for-each select="descendant::node">
          <xsl:call-template name="dump"/>
        </xsl:for-each>
        <xsl:text>&#10;descendants or self: </xsl:text>
        <xsl:for-each select="descendant-or-self::node">
          <xsl:call-template name="dump"/>
        </xsl:for-each>
        <xsl:text>&#10;preceding: </xsl:text>
        <xsl:for-each select="preceding::node">
          <xsl:call-template name="dump"/>
        </xsl:for-each>
        <xsl:text>&#10;following: </xsl:text>
        <xsl:for-each select="following::node">
          <xsl:call-template name="dump"/>
        </xsl:for-each>
      </xsl:template>
    
    </xsl:stylesheet>
    
    preceding siblings:  29 33
    following siblings:  41 45
    preceding and following siblings:  29 33 41 45
    preceding and following siblings with value A:  33 45
    following siblings:  41 45
    parent's children:  29 33 37 41 45
    parent's children except self:  29 33 41 45
    parent's children except self with value A:  33 45
    ancestors:  1 28
    immediate ancestor:  1
    ancestors or self:  1 28 37
    descendants:  38 39 40
    descendants or self:  37 38 39 40
    preceding:  2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 29 30 31 32 33 34 35 36
    following:  41 42 43 44 45 46 47 48