XSL: Call a more general template first each time a specific one is called

410 Views Asked by At

I have an existing xslt file that I've created before with several templates.

Now, the requirements have changed and now before processing one of these specific templates I have to check if the current evaluating node has a specific attribute or not.

If yes, another template has to be called first and then the specific one.

So basically my input XML looks like this:

<baseNode>
    <textGroup>
        <title>Title here</title>
        <text specAttr="id123456">Text here</text>
        <text>Another text</text>
    </textGroup>
    <paragraph specAttr="id123456">Other text here</paragraph>
    <references>
        <reference specAttr="id123456">
            <data title="1st Reference" ref="http://..." />
        </reference>
        <reference>
            <data title="2nd Reference" ref="http://..." />
        </reference>
        <reference specAttr="id123456">
            <data title="3rd Reference" ref="http://..." />
        </reference>
    </references>
</baseNode>

Now, parsing the XML file, the result should be something like this (actually the result contains FO-tags as I want to create a PDF via Apache FOP, this is just to simplify what I want to do):

<base>
    <title>Title here</title>
    <text>Next text has this attribute</text>
    <text>Text here</text>
    <text>Another text</text>
    <text>Next text has this attribute</text>
    <p>Other text here</p>
    <table>
        <head>
            <row>
                <cell>Title</cell>
                <cell>URL</cell>
            </row>
        </head>
        <body>
            <row>
                <cell col-span="2">
                    <text>Next text has this attribute</text>
                </cell>
            </row>
            <row>
                <cell>1st Reference</cell>
                <cell>http://...</cell>
            </row>
            <row>
                <cell>2nd Reference</cell>
                <cell>http://...</cell>
            </row>
            <row>
                <cell col-span="2">
                    <text>Next text has this attribute</text>
                </cell>
            </row>
            <row>
                <cell>3rd Reference</cell>
                <cell>http://...</cell>
            </row>
        </body>
    </table>
</base>

Well...there are several elements in the XML that can have this attribute 'specAttr' and I don't want to change each existing <xsl:template> to check for this attribute as well...

I tried to add a <xsl:template match="*"> which did that job and hoped it will match for each element before the specific template of this element is processed. But it seems as if this only works for nodes that does not a specific xsl:template defined.

I've also already read here that it is possible to create templates matching attributes with <xsl:template match="@specAttr">, but this template is only parsed if I add <xsl:apply-templates select="@*"/>. So I would also have to change each already existing template and add this line.

Is it possible to create something like a template-hierarchy or inheritance between templates? That always the parent template would be processed before the actual templat e of the current node?

Edit: Looking to the reference element you can see that I also have to know sometimes in which location I have to process the new template...If a reference has this attribute I have to create a complete new row with a column span of 2 (but sometimes in another table that could also be 4), because just adding the text node would not be enough...

Thinking about this I think there is no way to find a generic way to do this...

Or does anyone have another smart idea?

If you need more information or have questions, please don't hesitate to ask.

Thanks in advance.

BRgrds, Jens

1

There are 1 best solutions below

1
On

The xsl:next-match instruction can be useful here (or if you're in XSLT 1.0, xsl:apply-imports). This allows you to do something like

<xsl:template match="*[@specAttr]" priority="20">
  <doSomethingSpecial>
    <xsl:next-match/>
  </doSomethingSpecial>
</xsl:template>

where the next-match instruction invokes your existing rule, ie. the rule that would have matched in the absence of this one. (apply-imports is slightly different, the second rule has to be in an imported stylesheet).

This doesn't work quite so well if you need to insert something into the middle of the output created by the overridden template rule. But it's not clear from your problem statement whether you need to do that. If you do, it can sometimes be achieved by having the overriding template rule capture the output of the overridden template rule in a variable, and then transforming the content of the variable.