M Gilster
M Gilster

Reputation: 13

XSLT - Detecting text between elements

I am working on a transform to wrap consecutive elements of the same type into a single element, but only if there is no text between the nodes.

For example:

<foo>ABC</foo><foo>DEF</foo> <foo>GHI</foo>
  <foo>JKL</foo>

Should transform to:

<bar>
  <foo>ABC</foo>
  <foo>DEF</foo>
  <foo>GHI</foo>
  <foo>JKL</foo>
</bar>

Whitespace should be ignored whether it is a space or a return. Any other text should indicate an end to what should be wrapped. For example:

<foo>ABC</foo><foo>DEF</foo> some text in between <foo>GHI</foo>
  <foo>JKL</foo>

Should transform to:

<bar>
  <foo>ABC</foo>
  <foo>DEF</foo>
</bar> some text in between 
<bar>
  <foo>GHI</foo>
  <foo>JKL</foo>
</bar>

I have tried selecting the <foo> element using:

foo[following-sibling::node()[1][normalize-space()='']]

But this is not picking up the case where there is a space between the <foo> elements. (There are additional criteria in the full template match, but this is the part that is not working as I expect.

I wrote a test transform to output the value of using the normalize-space function on the text following the element, and it outputs a single space for the lines where there is a space between the elements. However, if I query for:

foo[following-sibling::node()[1][normalize-space()=' ']]

It does not find those elements. So, I'm totally confused.
Any help would be greatly appreciated!

Upvotes: 0

Views: 280

Answers (2)

M Gilster
M Gilster

Reputation: 13

The issue with [normalize-space()=''] or [not(normalize-space())] seeming to not work was because there were non-breaking spaces instead of regular spaces in a couple places. I updated the template match to use [not(normalize-space(translate(.,'&#160;', ' ')))] and it worked.

Upvotes: 1

Martin Honnen
Martin Honnen

Reputation: 167446

Assuming XSLT 2.0 you can use

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

    <xsl:template match="*[foo]">
        <xsl:copy>
            <xsl:for-each-group select="node()" group-adjacent="boolean(self::foo | self::text()[not(normalize-space())])">
                <xsl:choose>
                    <xsl:when test="current-grouping-key()">
                        <bar>
                            <xsl:apply-templates select="current-group()"/>
                        </bar>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:apply-templates select="current-group()"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:transform>

http://xsltransform.net/ncntCSS

Upvotes: 0

Related Questions