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

时间:2020-07-08 10:22:14

标签: xslt xslt-1.0

想象一下一个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:::在父级兄弟姐妹的轴上一直计数兄弟姐妹,直到根。

尝试3:preceding-sibling::

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

尝试4:generate-id()

如果我们放宽@position属性反映节点号的限制,则此方法稍有不同。只要生成的值是唯一的,这对我的设置就无关紧要。

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

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

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

这可能吗?

限制:

  • 我正在使用XSLT 1 / xsltproc
  • 生成的@position属性必须是唯一的
  • 生成的@position属性必须相对稳定,并且在运行或平台之间不得更改

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:1)

或者简单地

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>