0

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

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

我想要一个函数(自定义模板),对于具有属性 this="true" 的节点三,它会输出如下内容:“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>

under:如何在 XSLT 中输出当前元素路径?

通过几处修改,这将输出我需要的内容:

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

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

就像是:

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

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

4

4 回答 4

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。

于 2013-09-20T14:25:53.503 回答
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,您可以通过为每个祖先调用另一个模板来做到这一点

 <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>

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

 <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>

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

<xsl:apply-templates select="n1:tagname1/n1:tagname2" mode="genPath" />
于 2013-09-20T07:43:11.770 回答
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>
于 2013-09-20T07:42:37.740 回答
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>
于 2013-09-20T08:15:03.933 回答