我有以下XSL工作,但应该有一种方法来重写它而不使用xsl:for-each元素。我需要采用任意的XML块,删除所有具有“删除”功能的元素。属性并将剩余文本与空格连接,而不在连接文本的末尾或末尾添加额外的空格。
我应该说,我可以关键的XML的唯一部分是' drop'属性。我无法键入任何元素名称,如'测试','目录',#39;书'等。
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:for-each select="//*[not(@drop)]/text()">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
我一直在测试的XML:
<testing>
<text drop="meta">Drop this meta</text>
<catalog>
<book id="bk101">
<text drop="stuff">Drop this stuff</text>
<title ti="Full Title">XML Developer\ts Guide</title>
<author>Gambardella, Matthew</author>
</book>
</catalog>
</testing>
和有效输出:
XML Developer s Guide Gambardella, Matthew
编辑:正如@ michael.hor257k所指出的,它假设XML不会有任何mixed content
答案 0 :(得分:1)
此转化:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vFirst" select="(//*[not(@drop)]/text())[1]"/>
<xsl:template match="*[not(@drop)]/text()[not(. is $vFirst)]">
<xsl:value-of select="concat(' ', .)"/>
</xsl:template>
<xsl:template match="*[@drop]/text()"/>
</xsl:stylesheet>
应用于提供的源XML文档:
<testing>
<text drop="meta">Drop this meta</text>
<catalog>
<book id="bk101">
<text drop="stuff">Drop this stuff</text>
<title ti="Full Title">XML Developer s Guide</title>
<author>Gambardella, Matthew</author>
</book>
</catalog>
</testing>
生成想要的正确结果:
XML Developer s Guide Gambardella, Matthew
应用于更不规则的XML文档时,如下所示:
<testing>
<text drop="meta">Drop this meta</text>
<catalog>
<book id="bk100"><title ti="Full Title">JSON Developer s Guide</title></book>
<book id="bk101">
<text drop="stuff">Drop this stuff</text>
<title ti="Full Title">XML Developer s Guide</title>
<author>Gambardella, Matthew</author>
</book>
</catalog>
</testing>
转换再次产生正确的,想要的结果:
JSON Developer s Guide XML Developer s Guide Gambardella, Matthew
请注意,当应用于上述XML文档时,其他两个答案中的解决方案不会产生正确的结果。
答案 1 :(得分:1)
正如Martin Honnen在他删除的答案中指出的那样,在XSLT 2.0中,这可以通过单个xsl:value-of
指令来完成。这是他的回答,我做了一个小小的修正:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:value-of select="//*[not(@drop) and text()]" separator=" "/>
</xsl:template>
</xsl:stylesheet>
但是,这假定源XML中没有混合内容(我相信其他答案也是如此)。如果这是一项要求,您可以使用:
<xsl:value-of select="string-join(//*[not(@drop)]/text(), ' ')" />
代替。
答案 2 :(得分:0)
也许这就是你要找的东西(也适用于XSLT 1.0)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:template match="*[not(@drop) and normalize-space(text()) != '']" >
<xsl:value-of select="." />
<xsl:if test="position() != last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
更新: 正如评论中所提到的,position()的使用是而不是,因为它检查当前节点列表中的位置而不是文档中的位置。 因此尝试这个(仍然是XSLT 1.0解决方案):
<xsl:variable name="last" select="generate-id((//*[not(@drop)]/text())[last()])" />
<xsl:template match="*[not(@drop)]/text()[normalize-space() != '']" >
<xsl:value-of select="." />
<xsl:if test="generate-id() != $last">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:template>
<xsl:template match="text()" />