Philipp
Philipp

Reputation: 4749

XSLT generate-id() inside a predicate does not work

I'm using the generate-id() function inside a predicate to compare with a previously generated id. But this does not work and I can't figure out why.

I've created a sample XSLT with input and expected output. Yes I know - this code does seems weird and does not make sense, it is just to find out, why the generate-id() does not work here.

The problem line is the following:

<xsl:value-of select="a[generate-id(current())=$a_id]/text()"/>

This does not work. Here the complete XML input, XSLT and expected output.

XML Input:

<root>
    <test>
        <a>1</a>
        <a>2</a>
        <a>3</a>
    </test>
</root>

Expected Output:

<root>
    <test>
        <a>1</a>
    </test>
    <test>
        <a>2</a>
    </test>
    <test>
        <a>3</a>
    </test>
</root>

Result Output (the values are not found with the id search):

<root>
    <test>
        <a/>
    </test>
    <test>
        <a/>
    </test>
    <test>
        <a/>
    </test>
</root>

Here is the XSLT. The problem is in the last template:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
    <xsl:output indent="yes"></xsl:output>    

    <xsl:template match="/root">
        <xsl:copy>
            <xsl:apply-templates select="test"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="test">
        <xsl:for-each select="a">
            <xsl:apply-templates select=".." mode="output">
                <xsl:with-param name="a_id" select="generate-id()"/>
            </xsl:apply-templates>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="test" mode="output">
        <xsl:param name="a_id"/>
        <xsl:copy>
            <xsl:element name="a">
                <!-- Here the passed a_id cannot be found -->
                <xsl:value-of select="a[generate-id(current())=$a_id]/text()"/>
            </xsl:element>            
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Upvotes: 0

Views: 716

Answers (2)

Martin Honnen
Martin Honnen

Reputation: 167726

Inside of your template match="test", the current() function selects that matched test so I think you simply want

a[generate-id(.)=$a_id]

to make sure you compare the id of the a elements and not of the test with the a element. Note also that in XSLT 2.0 there is an is operator so you don't need to pass around ids and compare them, you could pass around the element directly and output it, or, if you need to compare some nodes based on identity, use that is operator.

Upvotes: 1

Tim C
Tim C

Reputation: 70648

The answer to your question is that you need to do this...

<xsl:value-of select="a[generate-id(.)=$a_id]/text()"/>

Or even this...

<xsl:value-of select="a[generate-id()=$a_id]/text()"/>

current() refers the the current node being matched, which is the test node. . refers the the current context (the a node). See Current node vs. Context node in XSLT/XPath? for more details.

Another answer would be that you may have over-complicated things. You could re-write your XSLT as this instead..

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
    <xsl:output indent="yes"></xsl:output>    

    <xsl:template match="/root">
        <xsl:copy>
            <xsl:apply-templates select="test"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="test">
        <xsl:for-each select="a">
            <test>
              <xsl:apply-templates select="." />
            </test>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

Upvotes: 1

Related Questions