meaulnes
meaulnes

Reputation: 383

How to test for existence of a node whose path is given in a variable

I am new to XSLT and not yet at ease with its syntax.

I have a variable (name="pageName") that contains an xpath to a page element in the xml source tree. For example:

/data/full-tree/page[@handle='en']/page[@handle='learn']/page[@handle='tutorials']

Before displaying a link to this page in the html page, I would like to be sure that the page element exist thus I write the following if test:

<xsl:if test="$pageName" >
    <a>
        <xsl:attribute name="href" >
            <xsl:value-of select='$theLink' />
        </xsl:attribute>
        Click here
    </a>
</xsl:if>

but it doesn't work as expected as the test result is always true even if the page element pointed by the xpath is not present in the tree. On the other hand, when I write the content of the variable directly into the test variable like this:

 <xsl:if test="/data/full-tree/page[@handle='en']/page[@handle='learn']/page[@handle='tutorials']
    <a>
        <xsl:attribute name="href" >
            <xsl:value-of select='$theLink' />
        </xsl:attribute>
        Click here
    </a>
</xsl:if>   

it seems to work and display the link when the page element exists but not when it the page element doesn't exist.

My question is how could I use my variable properly?

Edit

I will try to make my context more detailed

The xml source tree (that represents the structure of a web site is like this

<data>
<full-tree>
    <page handle="root" id="12">
        <name>Root Page</name>
        <types>
            <type>index</type>
        </types>
    </page>

    <page handle="en" id="1">
        <name>EN Root</name>
        <types>
            <type>en</type>
        </types>
        <page handle="home" id="3">
            <name>Home</name>
            <types>
                <type>en</type>
            </types>
        </page>
        <page handle="about" id="7">
            <name>About</name>
            <types>
                <type>en</type>
            </types>
            <page handle="about-zoraldia" id="8">
                <name>About zoraldia</name>
                <types>
                    <type>en</type>
                </types>
            </page>
        </page>
        <page handle="learn" id="21">
            <name>Learn</name>
            <types>
                <type>en</type>
            </types>
            <page handle="tutorials" id="22">
                <name>Tutorials</name>
                <types>
                    <type>en</type>
                </types>
                <page handle="symphony" id="24">
                    <name>Symphony</name>
                    <types>
                        <type>en</type>
                    </types>
                    <page handle="create-site" id="23">
                        <name>Create a bilingual Web Site with the Symphony CMS</name>
                        <types>
                            <type>en</type>
                            <type>web-site</type>
                        </types>
                    </page>
                    <page handle="symphony-menu-page" id="48">
                        <name>Add level 3 and 4 Menu Pages</name>
                        <types>
                            <type>en</type>
                            <type>symphony</type>
                        </types>
                    </page>
                </page>
            </page>
        </page>
        <page handle="think" id="49">
            <name>Think</name>
            <types>
                <type>en</type>
            </types>
            <page handle="free-software" id="50">
                <name>Free Software</name>
                <types>
                    <type>en</type>
                </types>
                <page handle="miscellaneous" id="51">
                    <name>Miscellaneous Articles</name>
                    <types>
                        <type>en</type>
                    </types>
                    <page handle="philo" id="52">
                        <name>Philosophy and Values of Free Software</name>
                        <types>
                            <type>en</type>
                        </types>
                    </page>
                </page>
            </page>
        </page>
    </page>
    <page handle="maintenance" id="9">
        <name>Maintenance notification</name>
        <types>
            <type>maintenance</type>
        </types>
    </page>
</full-tree>
<data>

This represents English pages but there are also a pending structure in French. What I want to do is when the visitor is on a French page, display an EN link only if the page has been translated.

Thus I use the French path (for example /fr/learn/tutorials and just exchange fr with en getting thus the /en/learn/tutorials into a $enLink variable.

My goal is to display this link that works properly only if the page exist.

I use a variable whose definition is

    <xsl:variable name="pending">
             <xsl:call-template name="build-xpath">
               <xsl:with-param name="output-string" select="'/data/full-tree'" />
               <xsl:with-param name="input-string" select="substring-after($enLink,'/')" />
           </xsl:call-template>
     </xsl:variable>

The build-xpath algorithm is the following:

<xsl:template name="build-xpath" >
    <xsl:param name="output-string" />
    <xsl:param name="input-string" />

     <xsl:choose>
         <xsl:when test="contains($input-string, '/')" >

            <xsl:variable name="new-output">
               <xsl:value-of select="$output-string" />/page[@handle='<xsl:value-of select="substring-before($input-string,'/')"/>']
            </xsl:variable>

            <xsl:variable name="new-input" >
                <xsl:value-of select="substring-after($input-string,'/')" />
            </xsl:variable >

            <xsl:call-template name="build-xpath" >
               <xsl:with-param name="output-string"  select="$new-output" />
               <xsl:with-param name="input-string" select="$new-input" />
            </xsl:call-template>


         </xsl:when>


         <xsl:otherwise><xsl:value-of select="$output-string"/>/page[@handle='<xsl:value-of select="$input-string"/>']
         </xsl:otherwise>


     </xsl:choose>


</xsl:template>

This algorithm returns a pending variable (the one I called pageName at first)

Displayed with

<xsl:value-of select="$pending" />

I see the string

/data/full-tree/page[@handle='en']/page[@handle='learn']/page[@handle='tutorials']

that is what I expect from the algorithm.

What puts me into trouble is that when I put this string into another variable, say anotherVar

<xsl:value-of select="$anotherVariable" 

doesn't display the string but the content of the node, in this case:

Tutorials en

This seems normal but why in the case of $pending it is the string itself that is displayed ?

Upvotes: 2

Views: 1436

Answers (1)

michael.hor257k
michael.hor257k

Reputation: 117165

Since your variable is a string, evaluating it in a Boolean test will always return true, because only an empty string evaluates as false.

In order to evaluate the variable as a node, you need to convert the string to an Xpath expression first. This can be done natively in XSLT 3.0. In previous versions, you need to use an extension function, such as the EXSLT dyn:evaluate() function - if your processor supports it.

There's probably a simpler way to handle this, but we don't see enough of the entire picture in order to be sure (I don't see any French elements in your input). I suspect that testing for the existence of a corresponding node by using some common ID, for example, would be much more straightforward.

Upvotes: 2

Related Questions