我想知道,如果有办法确定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">
(我知道上面的语法不正确)
答案 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) > 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>