XSLT:查找节点在给定节点集中的位置

XSLT: find position of node in a given nodeset

想象一棵 XML 树,其节点位于多个深度

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <ELEM2>
    <ELEM3/>
  </ELEM2>
  <ELEM2>
    <ELEM3/>
  </ELEM2>
  <ELEM2>
    <ELEM3/>
  </ELEM2>
</root>

我想知道每个元素在整棵树中的独特位置

期望输出:

  <?xml version="1.0"?>
  <root position="1"/>
    <ELEM2 position="2"/>
      <ELEM3 position="3"/>
    <ELEM2 position="4"/>
      <ELEM3 position="5"/>
    <ELEM2 position="6"/>
      <ELEM3 position="7"/>
    

所以,基本上,我想查看所有元素在列表中的位置,并使用该索引找到它。

尝试 1:position() 我试过这样使用 position()

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  
  <xsl:template match="*">
    <xsl:copy>
      <xsl:attribute name="position">
        <xsl:value-of select="position()"/>
      </xsl:attribute>
    </xsl:copy>
    <xsl:apply-templates select="node()"/>
  </xsl:template>
</xsl:stylesheet>

这给了我重复的位置属性:

  <?xml version="1.0"?>
  <root position="1"/>
    <ELEM2 position="2"/>
      <ELEM3 position="2"/>
    <ELEM2 position="4"/>
      <ELEM3 position="2"/>
    <ELEM2 position="6"/>
      <ELEM3 position="2"/>

因此,我无法在当前节点集中使用 position(),因为该位置仅限于树中的当前分支。

尝试 2:preceding::

然后我看着像这样使用 preceding::

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:template match="*">
    <xsl:copy>
      <xsl:attribute name="position">
        <xsl:value-of select="count(preceding::*)"/>
      </xsl:attribute>
    </xsl:copy>
    <xsl:apply-templates select="node()"/>
  </xsl:template>
</xsl:stylesheet>

但不幸的是我得到了这个:

<?xml version="1.0"?>
<root position="0"/>
  <ELEM2 position="0"/>
    <ELEM3 position="0"/>
  <ELEM2 position="2"/>
    <ELEM3 position="2"/>
  <ELEM2 position="4"/>
    <ELEM3 position="4"/>

因此,preceding:::计算 parents 兄弟轴上的兄弟姐妹,直到根。

尝试 3:preceding-sibling::

甚至不值得。这只会查看其他同级元素的位置,这意味着我将为每个 ELEM3 元素获得相同的位置。

尝试 4:generate-id()

如果我们放宽@position 属性反映节点编号的限制,则此方法略有不同。只要生成的值是唯一的,这在我的设置中并不重要。

这将生成唯一的@position 属性。不幸的是,id 只能保证在相同的 运行 中是相同的。这意味着我不能将其用于测试和我们的 CI/test 设置。

尝试 5:在给定列表中查找当前节点?

我在想是否可以通过 XPATH 以某种方式提供元素列表,如下所示:*ELEM2|ELEM3。现在,如果我可以获取 current() 节点并找到它在列表中的位置。

这可能吗?

限制:

使用<xsl:number count="*" level="any"/>:

https://xsltfiddle.liberty-development.net/gVhEaiH

或者干脆:

count(ancestor::*) + count(preceding::*) +1

这是一个完整的转换(只有 10 行):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="*">
    <xsl:copy>
      <xsl:attribute name="position" select="count(ancestor::*) + count(preceding::*) +1"/>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

应用于所提供的 XML 文档时:

<root>
    <ELEM2>
        <ELEM3/>
    </ELEM2>
    <ELEM2>
        <ELEM3/>
    </ELEM2>
    <ELEM2>
        <ELEM3/>
    </ELEM2>
</root>

产生了想要的、正确的结果:

<root position="1">
      <ELEM2 position="2">
            <ELEM3 position="3"/>
      </ELEM2>
      <ELEM2 position="4">
            <ELEM3 position="5"/>
      </ELEM2>
      <ELEM2 position="6">
            <ELEM3 position="7"/>
      </ELEM2>
</root>