1
votes

I have a problem and I need you.

I had an idea to solve it with the following code:

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

<xsl:template match="*[@foo]" priority="3">
    <p>This text must be systematically added for any rendered element having a foo attribute</p>
    <xsl:apply-templates select="."/>
</xsl:template>

<xsl:template match="bar1">
    <p>This is the normal rendering for the bar1 element</p>
</xsl:template>
<xsl:template match="bar1[@class='1']">
    <p>This is the normal rendering for the bar1 element with class 1</p>
</xsl:template>
<xsl:template match="bar1[@class='2']">
    <p>This is the normal rendering for the bar1 element with class 2</p>
</xsl:template>
...
<xsl:template match="barN">
    <p>This is the normal rendering for the barN element</p>
</xsl:template>

When I try to apply this xsl on the following xml:

<root>
    <bar1 foo="1"></bar1>
    <bar1 foo="1" class="1"></bar1>
    <bar1 class="2"></bar1>
    <bar1></bar1>
    ...
    <barN foo="n"></barN>
    <barN></barN>
</root>

The XSLT engine loops endlessly on the priority="3" template instead of (for my need) applying first the priority="3" template once and then applying the bar1 .. barN templates.

How can I perform this without modifying each of the bar1 .. barN templates (N~=150) to add the systematic text on each element having a foo attribute?

2

2 Answers

4
votes

<xsl:apply-templates select="."/> will always pick the most specific template, which may indeed lead to an infinite recursion. If you are using XSLT 2.0 then you can use

<xsl:next-match/>

instead, which picks the next-highest-priority template after the one that is currently executing. In XSLT 1.0 the only option is to move the generic templates into a different .xsl file, have your main one import that, and then use <xsl:apply-imports/> to apply templates with a "lower import precedence" (i.e. only consider the templates in the imported file, not the importing one).

classes.xsl

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:template match="bar1">
        <p>This is the normal rendering for the bar1 element</p>
    </xsl:template>
    <xsl:template match="bar1[@class='1']">
        <p>This is the normal rendering for the bar1 element with class 1</p>
    </xsl:template>
    <xsl:template match="bar1[@class='2']">
        <p>This is the normal rendering for the bar1 element with class 2</p>
    </xsl:template>
    ...
    <xsl:template match="barN">
        <p>This is the normal rendering for the barN element</p>
    </xsl:template>

</xsl:stylesheet>

main.xsl

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:import href="classes.xsl" />

    <xsl:template match="*[@foo]" priority="3">
        <p>This text must be systematically added for any rendered element having a foo attribute</p>
        <xsl:apply-imports/>
    </xsl:template>
</xsl:stylesheet>
0
votes

You could work with template modes.

<xsl:template match="*[@foo]">
    <p>This text must be systematically added for any rendered element having a foo attribute</p>
    <xsl:apply-templates select="." mode="normal" />
</xsl:template>

<xsl:template match="bar1" mode="normal">
    <p>This is the normal rendering for the bar1 element</p>
</xsl:template>

<!-- ... -->

<xsl:template match="barN" mode="normal">
    <p>This is the normal rendering for the barN element</p>
</xsl:template>

The point is, when <xsl:template match="*[@foo]"> matches something and you call a simple <xsl:apply-templates select="." /> inside it, the same template will of course match again.