在没有<xsl:for-each>的情况下重写;选择以空格分隔的所有文本;删除带有属性的文本

时间:2016-05-04 17:59:23

标签: xslt

我有以下XSL工作,但应该有一种方法来重写它而不使用xsl:for-each元素。我需要采用任意的XML块,删除所有具有“删除”功能的元素。属性并将剩余文本与空格连接,而不在连接文本的末尾或末尾添加额外的空格。

我应该说,我可以关键的XML的唯一部分是&#39; drop&#39;属性。我无法键入任何元素名称,如&#39;测试&#39;,&#39;目录&#39;,#39;书&#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

3 个答案:

答案 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()" />