如何在XSLT中获取给定元素的路径(数组子编号)

时间:2013-09-20 07:02:00

标签: xml xslt

我想知道,如果有办法确定xslt中给定源节点的路径。我们假设以下源XML文件:

<one>
 <two>
  <three/>
  <three this="true"/>
 </two>
 <two>
  <three/>
 </two>
</one>

我想要一个函数(自定义模板),对于具有属性this =“true”的节点3,将输出如下内容:“1,1,2” - 这意味着:从根开始进入第一个元素,然后是第一个元素,然后是第二个元素,以到达这个特定的节点。

(这是因为我想要从源XML文档中的特定位置获取内容的唯一标识符,并将其转换为我想要的输出)

编辑: 我发现了这个:

  <xsl:template name="genPath">
    <xsl:param name="prevPath"/>
    <xsl:variable name="currPath" select="concat('/',name(),'[',
      count(preceding-sibling::*[name() = name(current())])+1,']',$prevPath)"/>
    <xsl:for-each select="parent::*">
      <xsl:call-template name="genPath">
        <xsl:with-param name="prevPath" select="$currPath"/>
      </xsl:call-template>
    </xsl:for-each>
    <xsl:if test="not(parent::*)">
      <xsl:value-of select="$currPath"/>      
    </xsl:if>
  </xsl:template>

下:How do you output the current element path in XSLT?

通过几次修改,这将输出我需要的东西:

<xsl:call-template name="genPath">

某处,但这种调用方式会输出当前节点的路径。如何修改它以便能够写入当前节点的特定子节点的路径?

类似的东西:

<xsl:call-template name="genPath" select="n1:tagname/n1:tagname2">

(我知道上面的语法不正确)

4 个答案:

答案 0 :(得分:2)

这是我给学生的代码,它只用四行显示当前节点的XPath地址。 if之后的for-each语句使当前节点成为属性节点:

<!--walk down the ancestry exposing the elements-->
<xsl:for-each select="ancestor-or-self::*">
  <xsl:text/>/<xsl:value-of select="name(.)"/>
  <xsl:if test="parent::*">[<xsl:number/>]</xsl:if>
</xsl:for-each>
<xsl:if test="not(self::*)">
  <!--the current node must be an attribute-->
  <xsl:text/>/@<xsl:value-of select="name(.)"/>
</xsl:if>

请注意,递归调用是不必要的,因为可以简单地遍历树。

此代码适用于XSLT 1.0和XSLT 2.0。

答案 1 :(得分:1)

如果你使用的是XSLT2.0,你可以这样写:

 <xsl:template match="three[@this='true']">
    <xsl:value-of select="ancestor-or-self::*/(count(preceding-sibling::*) + 1)" separator="," />
 </xsl:template>

但是如果你只有XSLT 1.0,你可以通过为每个ancestort调用另一个模板来做到这一点

 <xsl:template match="three[@this='true']">
   <xsl:apply-templates select="ancestor-or-self::*" mode="position" />
 </xsl:template>

 <xsl:template match="*" mode="position">
    <xsl:if test="parent::*">,</xsl:if>
    <xsl:number count="*" />
 </xsl:template>

编辑:为了能够将其应用于任何元素,请将其放在与任何元素匹配的模板中,但具有mode属性。例如

 <xsl:template match="*" mode="genPath">
   <xsl:apply-templates select="ancestor-or-self::*" mode="position" />
 </xsl:template>

 <xsl:template match="*" mode="position">
    <xsl:if test="parent::*">,</xsl:if>
    <xsl:number count="*" />
 </xsl:template>

然后,要为任何“child”元素调用它,只需执行此操作

<xsl:apply-templates select="n1:tagname1/n1:tagname2" mode="genPath" />

答案 2 :(得分:0)

我不太清楚为什么我需要position() -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" />

    <xsl:template match="@*|node()">
        <xsl:apply-templates select="@*|node()" />
    </xsl:template>

    <xsl:template match="three[@this='true']">
        <!-- Loop through all parent elements above this element -->
        <xsl:for-each select="ancestor::node()">
            <xsl:if test="(position() - 1) &gt; 0">
                <xsl:value-of select="position() - 1" /><xsl:text>,</xsl:text>
            </xsl:if>
        </xsl:for-each>
        <!-- Current position of the found element -->
        <xsl:value-of select="position()" />
    </xsl:template>
</xsl:stylesheet>

答案 3 :(得分:0)

我明白了。

这会产生我需要的输出:

  <xsl:template name="genPath">
    <xsl:param name="prevPath"/>
    <xsl:param name="childNode"/>
    <xsl:if test="$childNode">
      <xsl:for-each select="$childNode">
        <xsl:call-template name="genPath"/>
      </xsl:for-each>
    </xsl:if>
    <xsl:if test="not($childNode)">
      <xsl:if test="$prevPath">
        <xsl:variable name="currPath" select="concat(count(preceding-sibling::*)+1,'-',$prevPath)"/>
          <xsl:for-each select="parent::*">
          <xsl:call-template name="genPath">
            <xsl:with-param name="prevPath" select="$currPath"/>
          </xsl:call-template>
        </xsl:for-each>
        <xsl:if test="not(parent::*)">
          <xsl:value-of select="$currPath"/>      
        </xsl:if>
      </xsl:if>
      <xsl:if test="not($prevPath)">
        <xsl:variable name="currPath" select="count(preceding-sibling::*)+1"/>
          <xsl:for-each select="parent::*">
          <xsl:call-template name="genPath">
            <xsl:with-param name="prevPath" select="$currPath"/>
          </xsl:call-template>
        </xsl:for-each>
        <xsl:if test="not(parent::*)">
          <xsl:value-of select="$currPath"/>      
        </xsl:if>
      </xsl:if>
    </xsl:if>
  </xsl:template>

现在我可以致电:

<xsl:call-template name="genPath">
  <xsl:with-param name="childNode" select="n1:tagname1/n1:tagname2"/>
</xsl:call-template>