1
votes

i am a beginner in xsl, so perhaps its a very easy question. i have a xsl file where i want to find a specific value thats is defined in a param-object:

<xsl:param name="locales">
    <label name="testname"><locale name="de">german text</locale><locale name="en">english text</locale></label>
</xsl:param>

<xsl:template match="foo">
    <topLevelElement xmlns="http://foo.bar.org">
        <xsl:value-of select="ext:node-set($locales)/label[@name='testname']/locale[@name='en']"/>
    </topLevelElement>
</xsl:template>

I expected to get the value 'english text' because i want to find in the param locales a label with the name testname. There I want to find a locale with the name en. But it doesn't work. When I replace the name of the specific elements (label and locale) by a star, then it works:

<xsl:param name="locales">
    <label name="testname"><locale name="de">german text</locale><locale name="en">english text</locale></label>
</xsl:param>

<xsl:template match="foo">
    <topLevelElement xmlns="http://foo.bar.org">
        <xsl:value-of select="ext:node-set($locales)/*[@name='testname']/*[@name='en']"/>
    </topLevelElement>
</xsl:template>

Can anyone tell me why I can't find the childs by its name? Thanks a lot!

Edit: The code doesn't show the namespace (its interpreted by the browser i guess). It is without the brackets at the bginning and the end:

xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://foo.bar.org" xmlns:ext="http://exslt.org/common" version="1.0" xsl:output method="xml" version="1.0" encoding="UTF-8"/

2
Let me guess: There is an xmlns="..." default namespace defined in your XSLT file.Tomalak
Yes i have. I have edited my question. So whats the problem with it? Does my label and locales now have another namespace so that i can't find them?Markus
Where is the function deceleration 'ext:node-set(... )'?Amrendra Kumar

2 Answers

3
votes

One way to fix it without changing the XPath is to change

<xsl:param name="locales">
    <label name="testname"><locale name="de">german text</locale><locale name="en">english text</locale></label>
</xsl:param>

to

<xsl:param name="locales" xmlns="">
    <label name="testname"><locale name="de">german text</locale><locale name="en">english text</locale></label>
</xsl:param>
4
votes

So you have

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://foo.bar.org"
    xmlns:ext="http://exslt.org/common"
>

and this defines a default namespace. Which means that every XML element your XSLT program creates (that isn't explicitly created with a namespace) will be in that namespace. This also applies to any element that you don't output.

And that means you cannot select these elements with "plain" XPath anymore, because plain XPath assumes that elements are in no namespace.

ext:node-set($locales)/label[@name='testname']/locale[@name='en']

looks for <label> elements that are in no namespace, and <locale> elements that are in no namespace. And the way to refer to a namespace in XPath is by using a prefix.

So you could give this namespace a prefix, just for internal handling.

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://foo.bar.org"
    xmlns:foo="http://foo.bar.org"
    xmlns:ext="http://exslt.org/common"
>

Here the "http://foo.bar.org" namespace is declared to be default, but also to have the prefix foo. Now you can do this:

ext:node-set($locales)/foo:label[@name='testname']/foo:locale[@name='en']

And things will start to work.


Personally, I would not use <xsl:param> for what you are doing. I would set up a supplementary XML document that has no namespaces, call it locales.xml, and then do:

<!-- top-level -->
<xsl:variable name="locales" select="document('locales.xml')" />

<!-- ...later -->
<xsl:value-of select="$locales/*/label[@name='testname']/locale[@name='en']" />

This way neither helper namespaces nor ext:node-set() are required, and you have increased modularity.