0

I have to make xPath dynamical, based on my request attributes in XSL file making JSP using expression language. So now I have this thing:

<xsl:variable name="xPath">@name='${categoryName}'</xsl:variable>
<xsl:for-each select="tar:products/category[$xPath]/subcategory">

But this doesn't work. So the question is how to get round this problem and make xPath work?


Input XML:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tar:products SYSTEM "products.dtd" >
<?xml-stylesheet type='text/xsl' href='products2HTML.xsl'?>
<tar:products xmlns:tar="http://www.epam.com/products"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.epam.com/products products.xsd ">
<category name="Electronics">
    <subcategory name="mp3">
        <product name="iPod 4g">
            <provider>Apple</provider>
            <model>Qd246</model>
            <dateOfIssue>11-11-2012</dateOfIssue>
            <color>white</color>
            <notInStock/>
        </product>
        <product name="Cowon">
            <provider>Cowon</provider>
            <model>sF223</model>
            <dateOfIssue>13-12-2012</dateOfIssue>
            <color>black</color>
            <price>13456</price>
        </product>
        <product name="IRiver">
            <provider>IRiver e30</provider>
            <model>Ir234</model>
            <dateOfIssue>13-12-2012</dateOfIssue>
            <color>black</color>
            <price>13456</price>
        </product>
    </subcategory>
    <subcategory name="Computers">
        <product name="iMac">
            <provider>Apple</provider>
            <model>Mm232</model>
            <dateOfIssue>20-02-2012</dateOfIssue>
            <color>silver</color>
            <notInStock/>
        </product>
        <product name="PC">
            <provider>Acer</provider>
            <model>Ae135</model>
            <dateOfIssue>22-11-2012</dateOfIssue>
            <color>black</color>
            <price>13456</price>
        </product>
    </subcategory>
</category>
<category name="Toys">
    <subcategory name="Electronical toys">
        <product name="Cat">
            <provider>UrkToy</provider>
            <model>cC246</model>
            <dateOfIssue>11-11-2012</dateOfIssue>
            <color>grey</color>
            <price>1100</price>
        </product>
        <product name="Dog">
            <provider>LatToy</provider>
            <model>Ld223</model>
            <dateOfIssue>03-12-2012</dateOfIssue>
            <color>white</color>
            <price>13456</price>
        </product>
    </subcategory>
    <subcategory name="Fabric toys">
        <product name="Bear">
            <provider>RusToy</provider>
            <model>BB122</model>
            <dateOfIssue>20-02-2012</dateOfIssue>
            <color>brown</color>
            <notInStock/>
        </product>
        <product name="Bird">
            <provider>BelToy</provider>
            <model>Bb110</model>
            <dateOfIssue>05-11-2012</dateOfIssue>
            <color>red</color>
            <price>500</price>
        </product>
    </subcategory>
</category>
</tar:products>

I put parameter categoryName in request from my previous page and it's successfully shown on my page, so problem is not in it. Here is more info about xsl file:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:tar="http://www.epam.com/products"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.epam.com/products products.xsd ">
<xsl:output method="html" />
<xsl:param name="categoryName" select="'${categoryName}'"></xsl:param>
<xsl:template match="/">
    <table border="1">
        <tr bgcolor="#CCCCCC">
            <td align="center">
                <strong>
                    Subcategories
                            </strong>
            </td>
        </tr>
        <xsl:value-of select="$categoryName"/><!-- this string works -->
        <xsl:for-each select="tar:products/category[@name=$categoryName]/subcategory">
            <xsl:sort order="descending" select="count(product)"
                data-type="number" />
            <a href="controller?command=products&amp;name={@name}">
            <tr bgcolor="#F5F5F5">
                <td>
                    <xsl:value-of select="@name" />
                    (
                    <xsl:value-of select="count(product)" />
                    )
                </td>

            </tr>
            </a>
        </xsl:for-each>
    </table>
    <form method="post" action="/controller?command=back">
        <input type="submit" value="Back"/>
    </form>
</xsl:template>
 </xsl:stylesheet>

Expected output,when request parameter categoryName equals 'Toys':

enter image description here


Actual output:

enter image description here


RESOVLED

In my java class I've put parameter in request before transformation, but needed simply to put it in transformator parameters:

transformer.setParameter("categoryName", name);
And
  • 343
  • 1
  • 5
  • 15

3 Answers3

2

If you really need to have dynamically built XPath, you will need to make use of an extension function. For example, you may be able to use the EXSTL Dynamic Extension Function.

However, looking at your rather terse code sample, I am not sure you need the extra complexity in this case. Could you two lines be simplified to the following:

<xsl:for-each select="tar:products/category[@name=$categoryName]/subcategory">
Tim C
  • 70,053
  • 14
  • 74
  • 93
  • Hello! Thanks for your time, but sorry, this doesn't work. It gives me blank list without any errors – And Jun 20 '12 at 07:06
  • btw, categoryName is a request parameter, which I'm trying to get with the help of EL. As far I understand you suggest to define it like xsl-variable. I did it but it doesn't work... – And Jun 20 '12 at 07:21
  • I think it would help if you expanded your question to give more details. Showing the input XML and expected output, for example, would make things more clear. – Tim C Jun 20 '12 at 07:45
2

In general, you can't do it. But there are vendor-specific solutions:

  1. Tim's reference to EXSLT is absolutely correct. (dyn:evaluate())
  2. XALAN has an evaluate function (refer: Using dynamic xpath in XSLT)
  3. Saxon has an evaluate function (http://saxon.sourceforge.net/saxon7.9/extensions.html#evaluate)

Also see Dimitre's answer to: dynamic xpath in xslt?

Also note, XSLT 3.0 will have and evaluate() function (http://www.w3.org/TR/xslt-21/#element-evaluate)

And finally, if your dynamic XPATH to be evaluated was constrained to be of a a very simple form (eg.: terms separated by the / operator, no axises and no predicates), then it would be possible to write a named template to perform the dynamic XPATH evaluation. But of course, what this template looks like will be highly dependant on your simple constraints on the expression.

Example:

Take your sample dynamic path:

<xsl:variable name="xPath">@name='${categoryName}'</xsl:variable>

Suppose that the attribute name part ('name' in this sample), and the value part (I think you meant $categoryName not ${categoryName} ) are both variable, but the overall form other expression, in other words: @some-name='some-string-value' is fixed. Then you could resolve the path expression like so:

<xsl:variable name="name" select="'name'" /><!-- or use another computed value. -->
<xsl:variable name="value" select="'my-category-name'" /><!-- or use another computed value. -->
<xsl:for-each select="tar:products/category[@*[name()=$name][.=$value]]/subcategory">
Community
  • 1
  • 1
Sean B. Durkin
  • 12,659
  • 1
  • 36
  • 65
1

It's always hard to answer a question where the requirement is written in terms of "imaginary" code - that is, code written in a language which isn't quite XSLT. So this answer assumes I have imagined correctly what you are trying to do. If that's the case, replace this:

<xsl:variable name="xPath">@name='${categoryName}'</xsl:variable>
<xsl:for-each select="tar:products/category[$xPath]/subcategory">

by this:

<xsl:for-each select="tar:products/category[@name = $categoryName]/subcategory">

For the more general problem, there is no standard facility in XSLT to generate XPath expressions dynamically from strings. But many products offer extension functions to achieve this: see for example saxon:evaluate().

Michael Kay
  • 156,231
  • 11
  • 92
  • 164