列出或计算从当前节点到具有某些属性的每个叶节点的路径

时间:2011-04-05 03:27:28

标签: xml xslt xpath stylesheet

这是使用XSLT列出或计数的某种方式 从当前节点到每个叶节点的路径 基于一些标准。 例如在特定情况下 这里假设当前节点是“t” 和从当前节点到每个叶节点的路径 “trg”属性。 在下面例如

<root>
  <t>
    <a1>
     <b1 trg="rr">
       <c1></c1>
     </b1>
     <b2>
       <c2></c2>
     </b2>
    </a1>
    <a2>
      <b3>
        <c3></c3>
      </b3>
    </a2>
  </t>
</root>

这里只有这个属性的路径 t / a1 / b2 / c2 t / a2 / b3 / c3

但不是 t / a1 / b1 / c1

4 个答案:

答案 0 :(得分:2)

最简单的方法是:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="text()"/>
    <xsl:template match="text()" mode="search"/>
    <xsl:template match="t">
        <xsl:apply-templates mode="search">
            <xsl:with-param name="pPath" select="name()"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="*" mode="search">
        <xsl:param name="pPath"/>
        <xsl:apply-templates mode="search">
            <xsl:with-param name="pPath" select="concat($pPath,'/',name())"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="*[not(*)]" mode="search">
        <xsl:param name="pPath"/>
        <xsl:value-of select="concat($pPath,'/',name(),'&#xA;')"/>
    </xsl:template>
    <xsl:template match="*[@trg]" mode="search" priority="1"/>
</xsl:stylesheet>

输出:

t/a1/b2/c2
t/a2/b3/c3

注意:完成拉式。路径搜索开始的规则(t patttern)。隧道参数(*模式)的规则。用于输出叶子路径(*[not(*)]模式)的规则。递归破坏条件的规则(*[@trg]模式)。

答案 1 :(得分:0)

我可以想办法实现这一点,但它看起来并不好看,所以希望有人会想出更好的方法......

首先,我使用当前节点的级别

定义了一个变量
<xsl:variable name="level">
   <xsl:value-of select="count(ancestor::*)"/>
</xsl:variable>

接下来,匹配所有后代或恰好是叶节点的当前节点,并且没有任何祖先(包括其自身,但只有当前级别),属性@trg ='rr'为指定。

<xsl:apply-templates select=".//*[not(node())][not(ancestor-or-self::*[count(ancestor::*) &gt; $level][@trg='rr'])]">
   <xsl:with-param name="currentLevel" select="$level"/>
</xsl:apply-templates>

匹配叶节点后,您应该能够将父节点的路径备份回当前节点。

这是完整的样式表

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <xsl:template match="/">
      <xsl:apply-templates select="root/t"/>
   </xsl:template>

   <xsl:template match="t">

      <!-- The level of the current node -->
      <xsl:variable name="level">
         <xsl:value-of select="count(ancestor::*)"/>
      </xsl:variable>

      <paths>
         <!-- Match all leaf nodes where there is not an ancestor (up to the current node) with the attribute @trg = 'rr' -->
         <xsl:apply-templates select=".//*[not(node())][not(ancestor-or-self::*[count(ancestor::*) &gt; $level][@trg=&apos;rr&apos;])]">
            <xsl:with-param name="currentLevel" select="$level"/>
         </xsl:apply-templates>
      </paths>
   </xsl:template>

   <!-- For leaf nodes, write out the ancestor path to a specified level -->
   <xsl:template match="*[not(node())]">
      <xsl:param name="currentLevel"/>
      <path>
         <xsl:for-each select="ancestor::*[count(ancestor::*) &gt;= $currentLevel]">
            <xsl:value-of select="name()"/>
            <xsl:text>/</xsl:text>
         </xsl:for-each>
         <xsl:value-of select="name()"/>
      </path>
   </xsl:template>

</xsl:stylesheet>

当应用于给定的XML时,将返回以下结果

<paths>
   <path>t/a1/b2/c2</path>
   <path>t/a2/b3/c3</path>
</paths>

答案 2 :(得分:0)

使用XSLT 2.0 / XPath 2.0

可以最好地解决此问题

非常初始的XSLT 1.0尝试

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:template match="*[not(*)]">
   <xsl:call-template name="buildPath">
    <xsl:with-param name="pstartNode" select="/*/t"/>
   </xsl:call-template>
 </xsl:template>

 <xsl:template match="text()"/>

 <xsl:template name="buildPath">
  <xsl:param name="pstartNode" select="."/>
  <xsl:param name="pendNode" select="."/>

  <xsl:choose>
   <xsl:when test=
    "not(count($pstartNode | $pendNode/ancestor-or-self::node())
        =
         count($pendNode/ancestor-or-self::node())
         )
    ">
   </xsl:when>
   <xsl:otherwise>
    <xsl:variable name="vPath" select=
      "ancestor::*
          [count(.|$pstartNode/descendant-or-self::*)
          =
           count($pstartNode/descendant-or-self::*)
          ]
      "/>
     <xsl:if test="not($vPath[@trg])">
      <xsl:for-each select=
        "ancestor::*
          [count(.|$pstartNode/descendant-or-self::*)
          =
           count($pstartNode/descendant-or-self::*)
          ]">
        <xsl:value-of select="concat(name(),'/')"/>
      </xsl:for-each>
      <xsl:value-of select="concat(name(), '&#xA;')"/>
     </xsl:if>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档

<root>
    <t>
        <a1>
            <b1 trg="rr">
                <c1></c1>
            </b1>
            <b2>
                <c2></c2>
            </b2>
        </a1>
        <a2>
            <b3>
                <c3></c3>
            </b3>
        </a2>
    </t>
</root>

生成想要的正确结果

t/a1/b2/c2
t/a2/b3/c3

答案 3 :(得分:0)

这是样式表:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <paths>
            <xsl:apply-templates select="root/t">
                <xsl:with-param name="path"/>
            </xsl:apply-templates>
        </paths>
    </xsl:template>

    <xsl:template name="path">
        <xsl:param name="path"/>
        <xsl:choose>
            <xsl:when test="string-length($path)">
                <xsl:value-of select="$path"/>/<xsl:value-of select="name()"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="name()"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="*">
        <xsl:param name="path"/>
        <xsl:if test="not(@trg)">
            <xsl:if test="not(count(*))">
                <path>
                    <xsl:call-template name="path">
                        <xsl:with-param name="path">
                            <xsl:value-of select="$path"/>
                        </xsl:with-param>
                    </xsl:call-template>
                </path>
            </xsl:if>
            <xsl:apply-templates select="*">
                <xsl:with-param name="path">
                    <xsl:call-template name="path">
                        <xsl:with-param name="path">
                            <xsl:value-of select="$path"/>
                        </xsl:with-param>
                    </xsl:call-template>
                </xsl:with-param>
            </xsl:apply-templates>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

这个想法非常简单 - 主要处理模板(math="*")是适用于每个节点的递归函数。它有一个参数 - path,它是从搜索根节点(t)到此节点的实际路径。一旦节点没有孩子 - 它可以输出。

另一个名为path的模板是一个简单的辅助函数,可以正确生成路径。如果你可以在你的XSLT处理器中注册一个XPath函数,我宁愿这样做,也不要去掉冗长的call-template结构。整个call-template ...调用可以是简单的<xsl:value-of select="myfn:path($path)"/>,这将大大提高可读性,这对XSLT非常重要;)

无论如何,我觉得我的解释中有太多路径字样了,所以不要犹豫,问一下是不是很清楚。