Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xsl:variable xsl:copy-of select

Tags:

xslt

I have the following XML:

<?xml version="1.0" encoding="UTF-8"?>
<XmlTest>
    <Pictures attr="Pic1">Picture 1</Pictures>
    <Pictures attr="Pic2">Picture 2</Pictures>
    <Pictures attr="Pic3">Picture 3</Pictures>
</XmlTest>

While this XSL does what is expected (output the attr of the first picture):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/XmlTest">
        <xsl:variable name="FirstPicture" select="Pictures[1]">
        </xsl:variable>
        <xsl:value-of select="$FirstPicture/@attr"/>
    </xsl:template>
</xsl:stylesheet>

It seems to be not possible to do the same inside the variable declaration using xsl:copy-of:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
    <xsl:template match="/XmlTest">
        <xsl:variable name="FirstPicture">
            <xsl:copy-of select="Pictures[1]"/>
        </xsl:variable>
        <xsl:value-of select="$FirstPicture/@attr"/>
    </xsl:template>
</xsl:stylesheet>

Curious: If I just select "$FirstPicture" instead of "$FirstPicture/@attr" in the second example, it outputs the text node of Picture 1 as expected...

Before you all suggest me to rewrite the code: This is just a simplified test, my real aim is to use a named template to select a node into the variable FirstPicture and reuse it for further selections.

I hope someone could help me to understand the behavior or could suggest me a proper way to select a node with code which could be easily reused (the decission which node is the first one is complex in my real application). Thanks.

Edit (thanks to Martin Honnen): This is my working solution example (which additionally uses a seperate template to select the requested picture node), using the MS XSLT processor:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    version="1.0">
    <xsl:template match="/XmlTest">
        <xsl:variable name="FirstPictureResultTreeFragment">
            <xsl:call-template name="SelectFirstPicture">
                <xsl:with-param name="Pictures" select="Pictures" />
            </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="FirstPicture" select="msxsl:node-set($FirstPictureResultTreeFragment)/*"/>
        <xsl:value-of select="$FirstPicture/@attr"/>
        <!-- further operations on the $FirstPicture node -->
    </xsl:template>

    <xsl:template name="SelectFirstPicture">
        <xsl:param name="Pictures"/>
        <xsl:copy-of select="$Pictures[1]"/>
    </xsl:template>
</xsl:stylesheet>

Not nice, that it is in XSLT 1.0 not possible to output a node directly from a template, but with the extra variable it is at least not impossible.

like image 444
Matthias Kientz Avatar asked Oct 06 '22 16:10

Matthias Kientz


1 Answers

Well with an XSLT 1.0 processor if you do

    <xsl:variable name="FirstPicture">
        <xsl:copy-of select="Pictures[1]"/>
    </xsl:variable>

the variable is a result tree fragment and all you can do with that in pure XSLT 1.0 is output it with copy-of (or value-of). If you want to apply XPath you first need to convert the result tree fragment into a node set, most XSLT 1.0 processors support an extension function for that so try

    <xsl:variable name="FirstPictureRtf">
        <xsl:copy-of select="Pictures[1]"/>
    </xsl:variable>
    <xsl:variable name="FirstPicture" select="exsl:node-set(FirstPictureRtf)/Pictures/@attr">

where you define xmlns:exsl="http://exslt.org/common" in your stylesheet.

Note that you will need to check whether your XSLT 1.0 processor supports the EXSLT extension function or a similar one in another namespace (as for instance the various MSXML versions do).

like image 164
Martin Honnen Avatar answered Oct 10 '22 11:10

Martin Honnen