如何索引/计数包含子节点的节点,格式为1,2,3,4



我试图找到一种方法来索引包含特定类型的子节点的节点,并将它们从1开始索引,并根据找到的每个子节点增加1。我尝试使用count(),但这显示了我的代码中节点的位置,而不是找到的子节点的索引。在其他语言中,我会使用变量,但在XSLT中不允许这样做。
我已经阅读了一些使用模板进行递归计数的示例,但是我对XSLT不够熟悉,无法将其集成到现有代码中。

XML

<?xml version="1.0" encoding="UTF-8"?>
<entry>
      <node>
           <subnodeA>test_subnodeA1</subnodeA>
           <subnodeB>test_subnodeB1</subnodeB>
      </node>
      <node>
           <subnodeA>test_subnodeA2</subnodeA>
           <subnodeB>test_subnodeB2</subnodeB>
      </node>
      <node>
           <subnodeA>test_subnodeA3</subnodeA>
      </node>
      <node>
           <subnodeA>test_subnodeA4</subnodeA>
           <subnodeB>test_subnodeB3</subnodeB>
      </node>
 </entry>
XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
    <!-- Initial usage -->
    <xsl:for-each select="node">
        <xsl:value-of select="subnodeA"/>
             <xsl:if test="subnodeB != ''">
                 <xsl:value-of select="position()"/>  
             </xsl:if>
    </xsl:for-each>
    <!-- Second usage -->
    <xsl:for-each select="node">
        <xsl:if test="subnodeB != ''">
            <xsl:value-of select="position()"/>. 
            <xsl:value-of select="subnodeB"/>
         </xsl:if>
    </xsl:for-each>
</xsl:template>

我想显示像

这样的内容
test_subnodeA1 1
test_subnodeA2 2
test_subnodeA3
test_subnodeA4 3
1 test_subnodeB1
2 test_subnodeB2
3 test_subnodeB3

但是使用我的方法我只能得到

test_subnodeA1 1
test_subnodeA2 2
test_subnodeA3
test_subnodeA4 4
1 test_subnodeB1
2 test_subnodeB2
4 test_subnodeB3

一些条目有几个节点没有subnodeB,然后有一个节点,所以我的列表从3,4或5开始。

在第一种情况下,您可以通过计算前面的兄弟节点来获得位置

<xsl:value-of select="count(preceding-sibling::node[subnodeA and subnodeB]) + 1" />
在第二种情况下,您只希望节点元素具有subnodeB
<xsl:for-each select="node[subnodeA and subnodeB]">

下面是XSLT示例。注意,我已经从使用xsl:for-each切换到使用xsl:apply-templates

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="text"/>
   <xsl:template match="/entry">
      <xsl:apply-templates select="node" mode="initial" />
      <xsl:apply-templates select="node[subnodeB]" mode="second" />
   </xsl:template>
   <xsl:template match="node" mode="initial">
      <xsl:value-of select="subnodeA" />
      <xsl:if test="subnodeB != ''">
      <xsl:text> - </xsl:text><xsl:value-of select="count(preceding-sibling::node[subnodeA and subnodeB]) + 1" />
      </xsl:if>
      <xsl:text>&#13;</xsl:text>
   </xsl:template>
   <xsl:template match="node" mode="second">
      <xsl:value-of select="position()" /> - <xsl:value-of select="subnodeB" /> 
      <xsl:text>&#13;</xsl:text>
   </xsl:template>
</xsl:stylesheet>

生成以下文本输出

test_subnodeA1 - 1
test_subnodeA2 - 2
test_subnodeA3
test_subnodeA4 - 3
1 - test_subnodeB1
2 - test_subnodeB2
3 - test_subnodeB3

我们也可以使用count直接在感兴趣的节点上应用模板:

给定subnodeA为当前节点:

  • count(preceding::subnodeB[not(../subnodeA)])不包含子节点a的所有前节点计数(当前移位)
  • count(preceding::subnodeA[../subnodeB])对所有具有subnodeB(相对位置- 1)的节点计数

让我们看看它如何使用重复(仅subnodeA,因为subnodeB是微不足道的):

<xsl:template match="entry">
    <xsl:for-each select="node/subnodeA">
        <xsl:value-of select="."/>
        <xsl:if test="../subnodeB">
            <xsl:value-of select="
                count(preceding::subnodeB[not(../subnodeA)])
                + count(preceding::subnodeA[../subnodeB])
                + 1 "/>
        </xsl:if>
        <xsl:if test="position()!=last()">
            <xsl:value-of select="'&#xA;'"/>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

和apply模板:

<xsl:template match="entry">
    <xsl:apply-templates select="node/subnodeA"/>
</xsl:template>
<xsl:template match="node/subnodeA">
    <xsl:value-of select="."/>
    <xsl:if test="../subnodeB">
        <xsl:value-of select="
            count(preceding::subnodeB[not(../subnodeA)])
            + count(preceding::subnodeA[../subnodeB])
            + 1 "/>
    </xsl:if>
    <xsl:if test="position()!=last()">
        <xsl:value-of select="'&#xA;'"/>
    </xsl:if>
</xsl:template>

这是最简单的解决方案之一——没有xsl:for-each,没有count(),没有特殊轴,根本没有显式条件逻辑:

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="/">
        <xsl:apply-templates select="*/node/subnodeA"/>
        <xsl:apply-templates select="*/node/subnodeB"/>
    </xsl:template>
    <xsl:template match="*[subnodeB]/subnodeA">
        <xsl:value-of select="concat(., ' ')"/>
        <xsl:number level="any" count="*[subnodeB]/subnodeA"/>
        <xsl:text>&#xA;</xsl:text>
    </xsl:template>
    <xsl:template match="subnodeB">
        <xsl:value-of select="concat(position(), ' ', ., '&#xA;' )"/>
    </xsl:template>
    <xsl:template match="text()">
        <xsl:value-of select="concat(., '&#xA;')"/>
    </xsl:template>
</xsl:stylesheet>

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

<entry>
    <node>
        <subnodeA>test_subnodeA1</subnodeA>
        <subnodeB>test_subnodeB1</subnodeB>
    </node>
    <node>
        <subnodeA>test_subnodeA2</subnodeA>
        <subnodeB>test_subnodeB2</subnodeB>
    </node>
    <node>
        <subnodeA>test_subnodeA3</subnodeA>
    </node>
    <node>
        <subnodeA>test_subnodeA4</subnodeA>
        <subnodeB>test_subnodeB3</subnodeB>
    </node>
</entry>

生成所需的正确结果:

test_subnodeA1 1
test_subnodeA2 2
test_subnodeA3
test_subnodeA4 3
1 test_subnodeB1
2 test_subnodeB2
3 test_subnodeB3

最新更新