我正在尝试将转换应用于xml,以便输出是所有子元素的xpath。例如:
<envelope>
<body>
<result>
<Testimonial>
<directional>
<allowed ><?xml version="1.0" encoding="utf-8"?> hi john <how is it></allowed>
<test><hi></test>
<test><hi></test>
</directional>
<directional>
<allowed ><?xml version="1.0" encoding="utf-8"?> hi john <how is it></allowed>
<test><hi></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())]) > 1">
<xsl:value-of select="concat('[', count(preceding-sibling::*[name()
= name(current())]) + 1, ']')"/>
</xsl:if>
<xsl:if test="count(../*[name() = name(current())]) <= 1">
<xsl:value-of select="'[1]'"/>
</xsl:if>
</xsl:variable>
<xsl:if test="preceding::*|ancestor::*">
<xsl:text> </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?
答案 0 :(得分:3)
您只需稍微更改输出逻辑 - 而不是
<xsl:if test="preceding::*|ancestor::*">
<xsl:text> </xsl:text>
</xsl:if>
<xsl:value-of select="$this-path"/>
尝试类似
的内容<xsl:if test="not(*)">
<xsl:value-of select="$this-path"/>
<xsl:text> </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> </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(), ' ')" />
</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>