I'd like to simulate a flag in an xslt script. The idea is for template foo to set a flag (or a counter variable, or anything), so that it can be accessed from template bar. Bar isn't called from foo, but from a common parent template (otherwise I would pass a parameter to it). The structure is like this:
<xsl:template match="bla">
<xsl:apply-templates select="foo"/> <!-- depending on the contents of foo... -->
<xsl:apply-templates select="bar"/> <!-- ... different things should happen in bar -->
</xsl:template>
Any tricks are much appreciated.
Not really... At least not in the sense that you are trying to do it. Variables in XSLT are immutable, once you assign them a value, you can't change them, so trying to call foo multiple times to change the value of the flag wouldn't work. There are a couple of patterns you could try, which might accomplish what you are trying to do, for example:
<xsl:variable name="myFlag"><xsl:apply-templates select="foo" /></xsl:variable>
<xsl:template match="bla">
<xsl:apply-templates select="bar" /> <!-- Can use the value of $myFlag --.
</xsl:template>
Which will work if template foo is built to return the value of the flag, however, if the value of the flag is meant to change over time, the only real way you can accomplish this is to incorporate the call to foo into the bar template.
<xsl:template match="bla">
<xsl:apply-templates select="bar"> />
</xsl:template>
<xsl:template match="bar">
<xsl:variable name="flag"><xsl:apply-templates name="foo" /></xsl:variable>
<xsl:if test="$flag=1">
</xsl:if>
</xsl:template>
There are plenty ways of doing this. For instance:
You can use conditional structures like xsl:if/xsl:choose.
You can use variables to store whatever is calculated from foo and pass it as parameter to the apply-templates on bar.
The true XSLT way whould be to define different templates for bar - which match different foo cases:
<xsl:template match="bar[../foo[#a='x']]">
...
</xsl:template>
<xsl:template match="bar[../foo[#a='y']]">
...
</xsl:template>
If template foo shall produce output, any solution using the output as flag won't work. In this case, if you are using a Java based XSLT processor (e.g. Saxon or Xalan), you can use mutable Java objects.
But note that this has its own difficulties. The transform given below uses a global flag, which might not suffice for all use cases. I would like to instantiate the flag in the bla template and pass it as parameter to foo and bar, but I could not get that working in Xalan. Also note that I invoke the Java setter in an xsl:value-of, because otherwise the call might get optimized away (see Cannot access updated Java object from Saxon XSLT processor).
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:myflag="java:mypackage.MyFlag">
<xsl:variable name="foo-flag" select="myflag:new()" />
<xsl:template match="bla">
<xsl:apply-templates select="foo"/> <!-- depending on the contents of foo... -->
<xsl:apply-templates select="bar"/> <!-- ... different things should happen in bar -->
</xsl:template>
<xsl:template match="foo">
<xsl:choose>
<xsl:when ...>
<xsl:value-of select="myflag:set($foo-flag, true())" />
...
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="myflag:set($foo-flag, false())" />
...
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="bar">
<xsl:choose>
<xsl:when test="myflag:get($foo-flag)">
...
</xsl:when>
<xsl:otherwise>
...
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:transform>
The class MyFlag in its most basic version is merely a mutable boolean wrapper.
public class MyFlag {
private boolean flag;
public void set(boolean flag){
this.flag = flag;
}
public boolean get(){ return flag; }
}
This uses the approach you mentioned in your question: Pass a parameter, from foo to bar. NOTE: This assumes that there is exactly one foo below each bla, or else the bar template will be invoked never or more than once per bar element.
<xsl:template match="bla">
<xsl:apply-templates select="foo" />
</xsl:template>
<xsl:template match="foo">
...
<xsl:apply-templates select="../bar">
<xsl:with-param name="flag" select="..." />
</xsl:apply-templates />
</xsl:template>
Related
I have this xsl template:
<xsl:template name="test">
I'm test
</xsl:template>
and I can call this template by calling
<xsl:call-template name="test"/>
QUESTION:
How to call xsl template by name stored in variable?
<xsl:variable name="var1" select="'test'"/>
<xsl:call-template name="$var1"/>
Instead of using xsl:call-template, try xsl:apply-templates.
<xsl:template name="test" match="test">
I'm test
</xsl:template>
<xsl:variable name="var1"><test/></xsl:variable>
<xsl:apply-templates select="$var1/*"/>
But in XSLT 3.0 you can do this much more cleanly using higher-order functions; functions become first-class values and can be bound to variables.
My code looks something like this (deliberate simplification):
<xsl:template match="/">
<xsl:call-template name="call_me" />
<xsl:value-of select="$inner_variable" />
</xsl:template>
<xsl:template name="call_me">
<xsl:variable name="inner_variable" select="/*" />
</xsl:template>
Obviously this won't work because of the scope of the variable. My question is:
is there any way to either define the variable globally from the call_me template, or to access this local variable from the parent (calling template) scope?
EDIT: The variable is not a XML tree fragment, but a node-set processed by C# functions. So I really need to access the variable.
Thanks!
If you want to have a variable with the result of the called template, do:
<xsl:template match="/">
<xsl:variable name="result">
<xsl:call-template name="call_me" />
</xsl:variable>
...
</xsl:template>
This assumes the called template returns some kind of result, for example:
<xsl:template name="call_me">
<xsl:value-of select="some-node" />
</xsl:template>
I try to create a variable, which I can use in a later template:
<xsl:variable name="fc">
<xsl:choose>
<xsl:when test="self::node()='element1'">gray</xsl:when>
<xsl:otherwise>red</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Unfortunately it does not work.
<xsl:template match="element1">
<h1><font color="{$fc}"><xsl:value-of select="self::node()"/></font></h1>
</xsl:template>
What am I doing wrong?
Here is the extensive code:
XML:
<root
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.test.com scheme.xsd" xmlns="http://www.test.com" xmlns:tst="http://www.test.com">
<elementA>
<elementB tst:name="name">
<elementC tst:name="name">
<element1> Test1 </element1>
<element2> Test2 </element2>
</elementC >
</elementB>
</elementA>
</root>
All the elements are qualified and part of the namespace "http://www.test.com".
XSLT:
<xsl:template match="/">
<html>
<body><xsl:apply-templates select="tst:root/tst:elementA/tst:elementB/tst:elementC/tst:element1"/>
</body>
</html>
</xsl:template>
<xsl:variable name="var_fc">
<xsl:choose>
<xsl:when test="local-name(.)='tst:element1'">gray</xsl:when>
<xsl:otherwise>red</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:template match="tst:element1">
<h2><font color="{$var_fc}"><xsl:value-of select="self::node()"/></font></h2>
</xsl:template>
As a result, element1 should turn gray, but it always turn red.
You can't use a variable for this, as the content of an xsl:variable is evaluated just once at definition time, whereas you want to evaluate some logic every time the variable is referenced, in the current context at the point of reference.
Instead you need a template, either a named one:
<xsl:template name="fc">
<xsl:choose>
<xsl:when test="local-name()='element1'">gray</xsl:when>
<xsl:otherwise>red</xsl:otherwise>
</xsl:choose>
</xsl:template>
or (better) a pair of matching templates with a mode, to let the template matcher do the work:
<!-- match any node whose local name is "element1" -->
<xsl:template mode="fc" match="node()[local-name() = 'element1']">gray</xsl:template>
<!-- match any other node -->
<xsl:template mode="fc" match="node()">red</xsl:template>
When you want to use this logic:
<h1>
<font>
<xsl:attribute name="color">
<xsl:apply-templates select="." mode="fc" />
</xsl:attribute>
Seeing as you have the tst prefix mapped in your stylesheet you could check the name directly instead of using the local-name() predicate:
<xsl:template mode="fc" match="tst:element1">gray</xsl:template>
<xsl:template mode="fc" match="node()">red</xsl:template>
XSLT variables are designed not to be changeable. Actually they could be named constants. If your variable fc is created global, it will use the root element for choose. You have to use choose in the actual template to be tested against the current element. If you want to have "red" and "gray" defined only once, create two variables with just that text content and use these instead the plain text in the choose.
Maybe it is a typo:
<xsl:when test=self::node()='element1'">gray</xsl:when>
should be:
<xsl:when test="self::node()='element1'">gray</xsl:when>
there is a missing quote.
I think instead of test="self::node()='element1'" you want test="self::element1" or test="local-name(.) = 'element1'".
A couple of other errors in your code:
(1) self::node() = 'element1'
tests whether the content of the element is "element1", not whether its name is "element1"
(2) local-name(.)='tst:element1'
will never be true because the local name of a node never contains a colon.
Experienced users would often write this code using template rules:
<xsl:template mode="var_fc" match="tst:element1">gray</xsl:template>
<xsl:template mode="var_fc" match="*">red</xsl:template>
and then
<xsl:apply-templates select="." mode="var_fc"/>
I want to dynamically change the apply-templates mode based on the source XML's attribute, like this:
<xsl:choose>
<xsl:when test="#myAttribute">
<xsl:apply-templates select="." mode="#myAttribute"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="." mode="someOtherMode"/>
</xsl:otherwise>
</xsl:choose>
Is it possible to evaluate the XPath in the mode attribute? Is there some other approach?
Thanks!
No, there isn't a way to use a dynamic value for the mode attribute. It has to be static. In your case, I would suggest doing something like this (using the name myNode as the context node for your example above):
<xsl:template match="myNode[#myAttribute = 'someValue']" mode="specialHandling">
<!-- template contents -->
</xsl:template>
<xsl:template match="myNode[#myAttribute = 'someOtherValue']" mode="specialHandling">
<!-- template contents -->
</xsl:template>
<xsl:template match="myNode[#myAttribute = 'aThirdValue']" mode="specialHandling">
<!-- template contents -->
</xsl:template>
<xsl:template match="myNode[not(#myAttribute)]" mode="specialHandling">
<!-- template contents -->
</xsl:template>
Then you don't even need that xsl:choose. You can just do:
<xsl:apply-templates select="." mode="specialHandling" />
I have an XML which might be like one of the following:
// #1
<A>
<B>... stuff ...</B>
</A>
// #2
<B>... stuff ...</B>
I need to transform these into a response node which should look the same for both instances. Sort of like this:
<fooMethodResponse>
... one thing from A if A was root ...
... stuff from B ...
</fooMethodResponse>
How can I do this simplest without repeating myself? I have done this now:
<xsl:template match="/A">
<fooMethodResponse>
<xsl:apply-templates select="B" mode="get-B" />
<xsl:element name="processId">
<xsl:value-of select="#id" />
</xsl:element>
</fooMethodResponse>
</xsl:template>
<xsl:template match="/B">
<fooMethodResponse>
<xsl:apply-templates select="." mode="get-B" />
</fooMethodResponse>
</xsl:template>
<xsl:template match="B" mode="get-B"></xsl:template>
Problem here is I'm repeating the response wrapper, and I'd like to only have that in one place. Figured I could do something like this:
<xsl:template match="/">
<fooMethodResponse>
<xsl:choose>
<xsl:when test="node name is A">
<xsl:when test="node name is B">
</xsl:choose>
</fooMethodResponse>
</xsl:template>
But I can't figure out how to write the test to check the node name of the root element. Are root elements handled differently somehow?
I'd like to give more precise examples, but is quite a bit of business stuff in there so I've tried to boil it down :p
I'm not sure about what you want to do, more precise input and output samples are needed.
Nevertheless, the following XSLT (1.0) can be a base to solve your problem :
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/">
<fooMethodResponse>
<xsl:apply-templates/>
</fooMethodResponse>
</xsl:template>
<xsl:template match="A">
<xsl:text>... one thing from A if A was root ...</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="B">
<xsl:text>... stuff from B ...</xsl:text>
</xsl:template>
</xsl:stylesheet>
For input #1 :
<A>
<B>... stuff ...</B>
</A>
Result #1 is :
<fooMethodResponse>... one thing from A if A was root ...
... stuff from B ...
</fooMethodResponse>
For input #2 :
<B>... stuff ...</B>
Result #2 is :
<fooMethodResponse>... stuff ...</fooMethodResponse>
Hope this helps!
What you can do is combine patterns with the | operator e.g.
<xsl:template match="/A[B] | /B">
<fooMethodResponse>...</fooMethodResponse>
</xsl:template>
Whether that makes sense or simplifies things in your case I am not sure as I don't understand what you want to put inside of fooMethodResponse for the two different elements. Consider to spell out each result sample for each possible input sample you have posted, your current single result sample is not clear to me.