XSLT:生成所有子元素的XPATH

时间:2014-04-16 11:51:15

标签: xml xslt

我正在尝试将转换应用于xml,以便输出是所有子元素的xpath。例如:

    <envelope>
        <body>
            <result>
                <Testimonial>
                    <directional>
                        <allowed >&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt; hi john &lt;how is it&gt;</allowed>
                        <test>&lt;hi&gt;</test>
                        <test>&lt;hi&gt;</test>
                    </directional>
                    <directional>
                        <allowed >&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt; hi john &lt;how is it&gt;</allowed>
                        <test>&lt;hi&gt;</test>
                    </directional>
                </Testimonial>
            </result>
        </body>
    </envelope>

应转换为:

/envelope[1]/body[1]/result[1]/Testimonial[1]/directional[1]/allowed[1] /envelope[1]/body[1]/result[1]/Testimonial[1]/directional[1]/test[1] /envelope[1]/body[1]/result[1]/Testimonial[1]/directional[1]/test[2] /envelope[1]/body[1]/result[1]/Testimonial[1]/directional[2]/allowed[1] /envelope[1]/body[1]/result[1]/Testimonial[1]/directional[2]/test[1]

下面是我的XSLT,我遇到的问题是我无法让它仅针对子元素。在目前的形式下,父母也包括在输出中,这不是我想要的。

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="text" media-type="text/plain"/>
        <xsl:template match="/">
            <xsl:apply-templates/>
        </xsl:template>

        <xsl:template match="*|@*">
            <xsl:param name="path" select="''"/>

            <xsl:variable name="this-path">
                <xsl:value-of select="concat($path, '/')"/>
                <xsl:if test="../@*[name() = name(current())]">
                    <xsl:text>@</xsl:text>
                </xsl:if>
                <xsl:value-of select="name()"/>
                <xsl:if test="count(../*[name() = name(current())]) &gt; 1">
                    <xsl:value-of select="concat('[', count(preceding-sibling::*[name()
    = name(current())]) + 1, ']')"/>
                </xsl:if>
                <xsl:if test="count(../*[name() = name(current())]) &lt;= 1">
                    <xsl:value-of select="'[1]'"/>
                </xsl:if>
            </xsl:variable>
            <xsl:if test="preceding::*|ancestor::*">
                <xsl:text>&#10;</xsl:text>
            </xsl:if>
            <xsl:value-of select="$this-path"/>
            <xsl:apply-templates select="*|@*">
                <xsl:with-param name="path" select="$this-path"/>
            </xsl:apply-templates>
        </xsl:template>
        <xsl:template match="comment()|text()|processing-instruction()"/>
    </xsl:stylesheet>

如何使输出成为子元素的xpath?

2 个答案:

答案 0 :(得分:3)

您只需稍微更改输出逻辑 - 而不是

<xsl:if test="preceding::*|ancestor::*">
    <xsl:text>&#10;</xsl:text>
</xsl:if>
<xsl:value-of select="$this-path"/>

尝试类似

的内容
<xsl:if test="not(*)">
    <xsl:value-of select="$this-path"/>
    <xsl:text>&#10;</xsl:text>
</xsl:if>

not(*)检查这是否是叶子节点(任何属性节点都是如此,以及没有子元素的任何元素节点都是。)

另外,您可以简化创建[number]的逻辑,因为>1<=1不需要两种情况。这同样适用于两者:

<xsl:value-of select="concat('[', count(preceding-sibling::*[name()
                                       = name(current())]) + 1, ']')"/>

并且../@*[name() = name(current())]也不是确定当前节点是否属于属性的可靠方法 - 在

之类的情况下会给出误报
<directional allowed="yes">
  <allowed>...</allowed>

更好的测试将是

count(../@* | .) = count(../@*)

利用XPath表达式选择节点,而不是列表 - count(x | y) = count(x)这一事实恰好在y是子集时x

或者更好的是,为属性节点创建一个单独的模板,您可以完全取出属性的条件逻辑。这是我的样式表版本,包含所有这些更改:

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

    <xsl:template match="*">
        <xsl:param name="path" select="''"/>

        <xsl:variable name="this-path"
          select="concat($path, '/', name(), '[',
              count(preceding-sibling::*[name() = name(current())]) + 1,
              ']')"/>
        <xsl:if test="not(*)">
            <xsl:value-of select="$this-path"/>
            <xsl:text>&#10;</xsl:text>
        </xsl:if>
        <xsl:apply-templates select="*|@*">
            <xsl:with-param name="path" select="$this-path"/>
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="@*">
        <xsl:param name="path"/>
        <xsl:value-of select="concat($path, '/@', name(), '&#10;')" />
    </xsl:template>

    <xsl:template match="comment()|text()|processing-instruction()"/>
</xsl:stylesheet>

答案 1 :(得分:0)

如果您测试是否有child元素,则可以跳过打印路径,并且只保留代表完整路径的五个字符串:

<xsl:if test="not(child::*)">
    <xsl:value-of select="$this-path"/>
</xsl:if>