Can an XSL template match in *ALL* modes? - xslt

Is there a way to write an XSL 1.0 template which is matching in all modes?
Or do I have to write a separate template for every existing mode (including additional templates for modes being added in the future)?
Here is what I have:
<xsl:apply-templates mode="mode1" />
...
<xsl:apply-templates mode="mode2" />
...
<!-- Do not process text content of nodes no matter in what mode -->
<!-- Is there a way to have only one template here? -->
<xsl:template match="text()" mode="mode1" />
<xsl:template match="text()" mode="mode2" />

The predefined mode: #all (only available in XSLT 2.0 however).
edit: replicating shared mode behaviour with 1.0
<xsl:template match="/">
<xsl:variable name="choice" select="'a'"/><!-- input seed here -->
<xsl:choose>
<xsl:when test="$choice='a'">
<xsl:apply-templates mode="a"/>
</xsl:when>
<xsl:when test="$choice='b'">
<xsl:apply-templates mode="b"/>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="*" mode="a">
[A]
<xsl:apply-templates />
</xsl:template>
<xsl:template match="*" mode="b">
[B]
<xsl:apply-templates />
</xsl:template>
<xsl:template match="text()">
[ALL]
</xsl:template>

Is there a way to write an XSL 1.0
template which is matching in all
modes
Yes, in order to do this one should follow these two rules:
Write your template without a mode attribute.
Within the moded templates have an <xsl:apply-templates> instruction without a mode attribute that will result in the template in 1. above being selected for processing
This follows directly from the XSLT 1.0 spec, which says:
If an xsl:apply-templates element has
a mode attribute, then it applies only
to those template rules from
xsl:template elements that have a mode
attribute with the same value; if an
xsl:apply-templates element does not
have a mode attribute, then it applies
only to those template rules from
xsl:template elements that do not have
a mode attribute.
To summarise: A set of templates each in a different mode can still issue <xsl:apply-templates> in such a way (described above), so that the same specific, single template will be selected for processing in each case.

If you want to have the template match in all modes then why are you using mode? If you don't use mode then the template will be used all the time. The reason for mode is to conditionally do different things with the same data type. Seems like you want modeless.

Related

XSL copy without values is it possible?

I want to compare two xmls.
1. First compare XML strucutre/schema.
2. Compare values.
I am using beyond compare tool to compare. Since these two xmls are different values, there are lot many differences in comparison report, for which I am not interested. Since, my focus now is to only compare structure/schema.
I tried to copy the xmls by following template, and other as well. But every time it is with values.
I surfed on google, xsl-copy command itself copies everything for selected node/element..
Is there any ways with which I can filter out values and only schema is copied ?
My Data :
<root>
<Child1>xxxx</Child1>
<Child2>yyy</Child2>
<Child3>
<GrandChild1>dddd<GrandChild1>
<GrandChild2>erer<GrandChild2>
</Child3>
</root>
Template used :
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<!-- for all elements (tags) -->
<xsl:template match="*">
<!-- create a copy of the tag (without attributes and children) in the output -->
<xsl:copy>
<!-- For all attributes of the current tag -->
<xsl:for-each select="#*">
<xsl:sort select="name( . )" order="ascending" case-order="lower-first" />
<xsl:copy/>
</xsl:for-each>
<!-- recurse through all child tags -->
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()|comment()|processing-instruction()">
<xsl:copy/>
</xsl:template>
OutPut Required :
Something like..
<root>
<Child1></Child1>
<Child2></Child2>
<Child3>
<GrandChild1><GrandChild1>
<GrandChild2><GrandChild2>
</Child3>
</root>
At the moment, you have a template matching text() to copy it. What you need to do is remove this match from that template, and have a separate template match, that matches only non-whitespace text, and remove it.
<xsl:template match="comment()|processing-instruction()">
<xsl:copy/>
</xsl:template>
<xsl:template match="text()[normalize-space()]" />
For white-space only text (as used in indentation), these will be matched by XSLT'S built-in templates.
For attributes, use xsl:attribute to create a new attribute, without a value, rather than using xsl:copy which will copy the whole attribute.
<xsl:attribute name="{name()}" />
Note the use of Attribute Value Templates (the curly braces) to indicate the expression is to be evaluated to get the string to use.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- for all elements (tags) -->
<xsl:template match="*">
<!-- create a copy of the tag (without attributes and children) in the output -->
<xsl:copy>
<!-- For all attributes of the current tag -->
<xsl:for-each select="#*">
<xsl:sort select="name( . )" order="ascending" case-order="lower-first" />
<xsl:attribute name="{name()}" />
</xsl:for-each>
<!-- recurse through all child tags -->
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="comment()|processing-instruction()">
<xsl:copy/>
</xsl:template>
<xsl:template match="text()[normalize-space()]" />
</xsl:stylesheet>
Also note that attributes are considered to be unordered in XML, so although you have code to sort the attributes, and they probably will appear in the right order, you can't guarantee it.

How best to re-use XSLT code when different groups of documents have different optional tag attributes for the same tags?

Here is the scenario. I have XML documents with tags that look like this:
<para a="A" b="B" c="C">
appearing in different classes of XML documents. The a and b attributes are completely generic and are handled exactly the same way in all documents. The optional c attribute is document class dependent, and will require different transforms, depending on the document class. I would like to write a stylesheet to be included or imported into document class-specific stylesheets which take care of doing the transform for and attributes a and b, which attribute c handled by the parent stylesheet. I can think of at least a couple of ways to do this, but am wondering if there is some canonical best way.
Let's call the stylesheet to be shared st-generic.xsl. Each of the templates in st-generic.xsl would be named:
<xsl:template match="para" name="generic-para">...</xsl:template>
The document class specific stylesheets would then import st-generic.xsl (rather than include, to set precedence), and would include templates that look like this:
<xsl:template match="para">
<xsl:call-template name="generic-para"/>
{other stuff}
<xsl:apply-templates/>
</xsl:template>
This probably works, but seems a bit inelegant. For example, in most cases the generic-para template is all that is needed, so this template will need to similarly include an
<xsl:apply-templates/>
node in the template body. I'm wondering if there is a better way to do this?
Without seeing more of your code and input, I don't see why you should use named templates.
Let's consider two hypothetical elements in your input:
<para a="A" b="B" c="C">paracontent</para>
<div a="A" b="B" c="C">divcontent</div>
Now, let us assume both attribute a and b are generic attributes that can be handled in the same way, no matter what element they occur on. c is processed in more than one way, depending on the parent element.
There is of course a template matching para elements
<xsl:template match="para">
and I don't see why you need a named template to process the attributes of this element. Why not simply apply-templates to all attributes?
<xsl:template match="para">
<xsl:apply-templates select="#*"/>
<!--Do stuff other than processing attributes...-->
</xsl:template>
Then, other templates (not named ones) would match the two generic attributes:
<xsl:template match="#a">
<!--Process attribute a, no matter the parent element-->
</xsl:template>
and
<xsl:template match="#b">
<!--Process attribute b, no matter the parent element-->
</xsl:template>
or perhaps even
<xsl:template match="#a|#b">
<!--Process attributes a or b, no matter the parent element-->
</xsl:template>
whereas you would write separate templates for attribute c:
<xsl:template match="para/#c">
<!--Process attribute c, if para is the parent-->
</xsl:template>
and
<xsl:template match="div/#c">
<!--Process attribute c, if div is the parent-->
</xsl:template>
All of this code is still in separate templates and can be modularized and imported or included ad libitum.
The best I can think of at the moment is:
Include this in your st-generic.xsl file:
<xsl:template match="para">
{ do para processing }
<xsl:apply-templates select="para" mode="custom" />
<xsl:apply-templates />
</xsl:template>
<xsl:template match="para" mode="custom" priority="-5" />
Then when you need custom behavior, you can put this in your main template:
<xsl:template match="para" mode="custom">
{ do custom para processing }
</xsl:template>
this will be invoked between the { do para processing } and the <xsl:apply-templates /> in the generic file, so you can have the custom template focus on custom behavior.
XSL provides a canonical way of solving this problem using the <xsl:apply-imports/> element.
In this particular example, you would have the st-generic.xsl stylesheet import a customizations.xsl stylesheet which would include optional customizations:
st-generic.xsl:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="customizations.xsl"/>
<xsl:template match="para">
<xsl:if test="#a and string(#a)">
{do stuff}
</xsl:if>
<xsl:if test="#b and string(#b)">
{do other stuff}
</xsl:if>
<xsl:apply-imports/>
<xsl:apply-templates/>
</xsl:template>
/xsl:stylesheet>
customizations.xsl:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="para">
<xsl:if test="#c and string(#c)">
{do special stuff}
</xsl:if>
</xsl:template>
/xsl:stylesheet>
A completely flexible solution is provided if you also name the templates in the st-generic.xsl stylesheet, which then allows you to import st-generic.xsl into yet another stylesheet, calling the named templates as needed or using <xsl:apply-imports/> as used in the previous example.
So, my original answer proved not to work because of the unfortunate way that <xsl:apply-imports/> works when there is no matching template. See the answer to this question for details. The bottom line is it resorts to default templates, which ends up messing the output when you use it the way I had it in my original answer.
In pondering what to do about this I came up with an alternative solution I like better anyway, namely use attribute sets defined in an included stylesheet. For example, to handle the case proposed in the original question, you could do something like this:
The main stylesheet would look like this:
<xsl:include href="generic-attributes.xsl"/>
<xsl:template match="para">
<fo:block xsl:use-attribute-sets="para_default-attrs">
<xsl:if test="#a and string(#a)">
{do stuff}
</xsl:if>
<xsl:if test="#b and string(#b)">
{do other stuff}
</xsl:if>
~~ other stuff ~~
<xsl:apply-templates/>
</fo:block>
</xsl:template>
The generic-attributes.xsl file would then contain this:
<xsl:attribute-set name="para_default-attrs">
<xsl:attribute name="a">A</xsl:attribute>
<xsl:attribute name="b">B</xsl:attribute>
</xsl:attribute-set>

choosing to have calls to different templates in xsl

I have the below template that is xsl:apply template
<xsl:apply-templates
select="fpml:dataDocument/fpml:trade/fpml:swap/fpml:swapStream/fpml:payerPartyReference[starts-with(#href, 'PO')]" />
as shown above it is working fine for 'PO' now i want to make it for CPTY too so I have developed it as shown..
<xsl:apply-templates
select="fpml:dataDocument/fpml:trade/fpml:swap/fpml:swapStream/fpml:payerPartyReference[starts-with(#href, 'CPTY')]" />
but the problem is that there can't be two seprate templates with the same name payerPartyReference can you please advise what is the best approach to deal with this ..
what approach i am thinking is ..
<xsl:if test="fpml:dataDocument/fpml:trade/fpml:swap/fpml:swapStream/fpml:payerPartyReference[starts-with(#href, 'PO')]">
</xsl:if>
<xsl:if test="fpml:dataDocument/fpml:trade/fpml:swap/fpml:swapStream/fpml:payerPartyReference[starts-with(#href, 'CPTY')]">
</xsl:if>
You're right that you can't have two templates with exactly the same matching pattern, but you can have
<xsl:template match="fpml:payerPartyReference[starts-with(#href, 'PO')]">
<!-- ... -->
</xsl:template>
<xsl:template match="fpml:payerPartyReference[starts-with(#href, 'CPTY')]">
<!-- ... -->
</xsl:template>
With these separate templates in place you may find you don't need to split out the apply-templates. Depending on the precise details of your problem you might find that you can just do one
<xsl:apply-templates
select="fpml:dataDocument/fpml:trade/fpml:swap/fpml:swapStream/fpml:payerPartyReference" />
and let the template matcher handle the conditional behaviour by picking the appropriate matching template for each target node.

XSLT: apply a template to a tree fragment

I have defined a variable $NodeVariable,
for instance:
<xsl:variable name="NodeVariable">
<aT>
<aT2>foo</aT2>
<aT3>bar</aT3>
</aT>
</xsl:variable>
and in different parts of the code I want to "apply"
different templates to myVariable.
Unfortunately,
I don't know what's the syntax for this.
I've tried the following:
<xsl:for-each select="$NodeVariable">
<xsl:call-template name="ns:ExtractInfo1"/>
</xsl:for-each>
<xsl:copy-of select="$NodeVariable">
<xsl:call-template name="ns:ExtractInfo2"/>
</xsl:for-each>
<xsl:copy-of select="$NodeVariable">
<xsl:call-template name="ns:ExtractInfo3"/>
</xsl:for-each>
which doesn't work.
How to apply a template to a tree fragment?
Assuming you use an XSLT 1.0 processor, you need to convert the result tree fragment to a node set first:
<xsl:variable name="NodeVariable">
<aT>
<aT2>foo</aT2>
<aT3>bar</aT3>
</aT>
</xsl:variable>
<xsl:variable name="NodeSet" select="exsl:node-set($NodeVariable)"/>
(where the stylesheet declares xmlns:exsl="http://exslt.org/common"), then you can apply-templates in different modes as needed e.g.
<xsl:apply-templates select="$NodeSet/aT" mode="m1"/>
and write templates for that mode e.g.
<xsl:template match="aT" mode="m1">
<xsl:value-of select="aT2"/>
</xsl:template>
Of course if you really want to call named templates you could do that as well, but using apply-templates and modes for different processing steps is the preferred way in XSLT in my view.

xslt pass parameters throuh all apply-template calls

In a rather complex xslt file some elements are to be processed twice. This is done by a template with a paramater.
<xsl:template macht="x">
<xsl:param name="modus"/>
<!-- comon things to do for both cases -->
<xsl:choose>
<xsl:when test="$modus='case1'"> <!-- things to do in case 1 --> </xsl:when>
<xsl:when test="$modus='case2'"> <!-- things to do in case 2 --> </xsl:when>
</xsl:choose>
</xsl:template>
The problem is: I cannot simply apply or call this template directly. The element x (for which this template with these two cases is for) is often at a quite low level of the xml input. Almost all ancestor elements have to be processed (in both cases) before it actually comes to x.
The call for the two cases is at the allmost top level.
In html it would be like this
<body>
<h1>Case 1</h1>
<xsl:apply-templates><xsl:with-parameter name="modus" select="case1"/>
<h1>Case 2</h1>
<xsl:apply-templates><xsl:with-parameter name="modus" select="case2"/>
</body>
So. How can I make sure, that the parameter reaches the template for x?
Of course, I could replace all
<xsl:apply-templates/>
calls within the templates for every single ancestor element of x by
<xsl:param name="modus">
<!-- What ever content here -->
<xsl:apply-templates><xsl:with-parameter name="modus" select="$modus"/></apply-templates>
But that would mean a lot of effort. Is there a better way to do this?
XSLT 2.0 has tunnel parameters e.g. with
<xsl:apply-templates>
<xsl:with-param name="modus" tunnel="yes" select="'foo'"/>
</xsl:apply-templates>
and
<xsl:template match="bar">
<xsl:param name="modus" tunnel="yes"/>
...
</xsl:template>
you don't have to pass on the parameter explicitly in the templates for ancestors of bar. So using an XSLT 2.0 processor like Saxon 9 you can do that.