Rudramuni TP
Rudramuni TP

Reputation: 1278

XSLT, how to find preceding sibling's (text contained) last most child element with text?

In the following XML and XSLT, in xml where element 'i' is present location, by there I need to find the preceding-siblings last child text.

Input1.xml

  <root>
    <aa>
    <a>
        <b>Text1</b>
        <c>Text2</c>
        <d>Text3
            <n>Text11
                <o>Text12
                    <p>Text13</p>
                </o>
            </n>
        </d>
    </a>
    <e>
        <f>
            <g>Text4</g>
        </f>
        <h>Text5
            <j>Text7</j>
            <k>Text8</k>
        </h>
    </e>
    <i>Text6</i>
    </aa>
  </root>

Child.xsl

  <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="*">
        <xsl:element name="{local-name()}">
            <xsl:apply-templates select="@* | node()"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="@*">
        <xsl:attribute name="{local-name()}">
            <xsl:value-of select="."/>
        </xsl:attribute>
    </xsl:template>

    <xsl:template match="i">
        <xsl:variable name="var0"><xsl:apply-templates select="preceding-sibling::*[1]"/></xsl:variable>
        <xsl:variable name="var1">
            <xsl:for-each select="var0">
                <xsl:value-of select="child::*[last()]"/>
            </xsl:for-each>
        </xsl:variable>

        <xsl:variable name="var2">
            <xsl:for-each select="$var1">
                <xsl:value-of select="child::*[last()]"/>
            </xsl:for-each>
        </xsl:variable>

            <ppp><xsl:copy-of select="."/><jj>-The last preceding child text is <xsl:value-of select="$var2"/>-</jj></ppp>
    </xsl:template>
  </xsl:stylesheet>

Required output is

Text6The last preceding child text is - Text8

Upvotes: 1

Views: 3795

Answers (2)

keshlam
keshlam

Reputation: 8058

It looks like you mean previous sibling's last descendent non-whitespace, not last child text. (Remember, the indendation you've shown us in the XML file is also text nodes.)

Get the preceding sibling element. Get its text-node descendants. From those select the non-whitespace ones. From those, select the last one.

Should be something like:

select="preceding-sibling::*/descendant::text()[normalize-space()!=''][last()]"

(where I'm taking advantage of the fact that the principal node type of preceding-sibling is element).

That finds the text node. To get its containing element, add one more step to the path:

select="preceding-sibling::*/descendant::text()[normalize-space()!=''][last()]/parent::*"

(again, taking advantage of the fact that the principal node type of parent is element).

If you're going to work with XPath and XSLT, it's worth spending some time really getting used to predicates. They're what permits layering complex constraints into a single path, and they add a LOT of power to the language.

Upvotes: 7

Alexis Wilke
Alexis Wilke

Reputation: 20741

To test whether a node is of type text you add text() after the selector:

preceding-sibling::text()

And it seems to me that all you need in var2 is this:

preceding::text()

and var2 will be the text node you're looking for.

Upvotes: 2

Related Questions