How to recursively join all texts of all preceding siblings?

This is my XML:

<?xml version="1.0"?>
<foo>
  <a>
   first
   <b>x</b>
   <c>y</c>
  </a>
  <b>second</b>
  <bar>hey</bar>
</foo>

This is my XSL:

<?xml version="1.0"?>
<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:template match="/foo">
    <xsl:apply-templates select="bar" />
  </xsl:template>
  <xsl:template match="bar">
    [<xsl:value-of select="preceding-sibling::text()"/>]
  </xsl:template>
</xsl:stylesheet>

I’m expecting this:

[first x y second]

However, it doesn’t work as expected. Simply put, I’m interested to join all texts (recursively) of all siblings of the current node.

>Solution :

The siblings which you select are not themselves text nodes, therefore you cannot use the text() node test immediately after the preceding-sibiling:: axis. Instead, you must select text descendants of these siblings.

You probably also want to omit whitespace-only texts such as the one between the <b> and <c> elements:

[<xsl:for-each select="preceding-sibling::*//text()
  [normalize-space(.)!='']"> <!-- filter out whitespace-only -->
  <xsl:if test="position()>1">
    <xsl:text> </xsl:text>
  </xsl:if>
  <xsl:value-of select="normalize-space(.)"/>
</xsl:for-each>]

If you want the texts separated by one space, you must insert that explicitly and remove trailing and leading whitespace with the normalize-space function.

(XSLT 1.0)

Leave a Reply