Reputation: 99
I have the following XSL working but there should be a way to rewrite it without using the xsl:for-each element. I need to take an arbitrary block of XML, drop all elements that have a 'drop' attribute and concatenate the remaining text with spaces without adding extra spaces at the end or end of the concatenated text.
I should say that the only part of the XML I can key off of is the 'drop' attribute. I cannot key off of any of element names like 'testing', 'catalog', 'book', etc.
<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>
The XML I've been testing with:
<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>
and the valid output:
XML Developer s Guide Gambardella, Matthew
edit: As pointed out by @michael.hor257k it assumed the XML will not have any mixed content
Upvotes: 2
Views: 665
Reputation: 9627
Perhaps this is what you are looking for (works also with 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>
Update: As mentioned in comment the use of position() was not right because it checks the position in the current node-list and not in the document. Therefore try this (still XSLT 1.0 solution):
<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()" />
Upvotes: 0
Reputation: 117165
As Martin Honnen pointed out in his deleted answer, in XSLT 2.0 this could be accomplished by a single xsl:value-of
instruction. Here's his answer, with a minor correction by me:
<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>
However, this assumes no mixed content in the source XML (as I believe the other answers do too). If this were a requirement, you could use :
<xsl:value-of select="string-join(//*[not(@drop)]/text(), ' ')" />
instead.
Upvotes: 1
Reputation: 243599
This transformation:
<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>
when applied on the provided source XML document:
<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>
produces the wanted, correct result:
XML Developer s Guide Gambardella, Matthew
When applied on more irregular XML documents, like this:
<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>
the transformation again produces the correct, wanted result:
JSON Developer s Guide XML Developer s Guide Gambardella, Matthew
Do note that the solutions in the other two answers don't produce the correct result when applied on the above XML document.
Upvotes: 1