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"/>
Related
i would like to check a sequence of Items and report the item type:
If it is an atomic value (maybe an empty string) report String "A"
If it is an Element report its local Name
Otherwise report String "X"
My Question is: How can i check an item to be an atomic value? The only Solution that i found is to use the xs:string(.) function, which takes an anyAtomic as argument. See the following XSLT 3.0 fragment
<xsl:template match="/*">
<xsl:variable name="content" as="item()*">
<xsl:apply-templates select="node()"/>
</xsl:variable>
<result>
<xsl:for-each select="$content">
<xsl:choose>
<!-- I`d like to check for any atomic value here -->
<xsl:when test="xs:string(.)">A</xsl:when>
<xsl:when test="self::*"><xsl:value-of select="lower-case(local-name())"/></xsl:when>
<xsl:otherwise>X</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</result>
</xsl:template>
<xsl:template match="*">
<xsl:copy/>
</xsl:template>
<xsl:template match="text()" as="xs:string">
<!-- What if i change to normalize-space(.) ? -->
<xsl:sequence select="."/>
</xsl:template>
Problem is, that i get an error when i use the normalize-space() function in the template matching text-Node, because in this case the effective boolean value of xs:string() is false for some text Nodes. The following check for the self Axis fails with the error message XPTY0020: The required item type of the context item for the self axis is node(); the supplied value "" is an atomic value.
I have tried with expressions like ". is anyAtomic", but the IS Operator expects Nodes.
I am pretty sure that xs:string(.) is not the correct way to check for atomic items, but what is it?
Thanks,
Frank Steimke
XPath has an instance of operator you can use with sequence types so I think you are looking for . instance of xs:anyAtomicType or e.g. . instance of xs:string.
So I'm trying to create a variable and then later on use this in my template.
This is the code I have now:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="items">
<xsl:call-template name="item" />
</xsl:template>
<xsl:template name="item">
<xsl:for-each select="item">
<xsl:variable name="currentItemTheme">
test variable
</xsl:variable>
<xsl:if test="title = name">
Showvariable: {$currentItemTheme} <br/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I have no idea why it doesn't work. My file does output the "Showvariable: ", but it just doesn't show the test values. So the for-each loops and template aren't the problem.
Is there something I'm missing? Does someone know how I get this to work?
I think you want <xsl:value-of select="$currentItemTheme"/> instead of {$currentItemTheme}, unless you are mixing the XSLT code with some other language that provides the {$currentItemTheme} syntax.
<xsl:copy-of select="$currentItemTheme"/> is another option if you build nodes in your variable and want to output them instead of just the string value as a text node.
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.
for performance testing purposes I want to take a small XML file and create a bigger one from it - using XSLT. Here I plan to take each entity (Campaign node in the example below) in the original XML and copy it n times, just changing its ID.
The only way I can think of to realize this, is a xsl:for-each select "1 to n". But when I do this I do not seem to be able to access the entity node anymore (xsl:for-each select="campaigns/campaign" does not work in my case). I am getting a processor error: "cannot be used here: the context item is an atomic value".
It seems that by using the "1 to n" loop, I am loosing the access to my actual entity. Is there any XPath expression that gets me access back or does anyone have a completely different idea how to realize this?
Here is what I do:
Original XML
<campaigns>
<campaign id="1" name="test">
<campaign id="2" name="another name">
</cmpaigns>
XSLT I try to use
<xsl:template match="/">
<xsl:element name="campaigns">
<xsl:for-each select="1 to 10">
<xsl:for-each select="campaigns/campaign">
<xsl:element name="campaign">
<xsl:copy-of select="#*[local-name() != 'id']" />
<xsl:attribute name="id"><xsl:value-of select="#id" /></xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:for-each>
</xsl:element>
</xsl:template>
Define a variable as the first thing in the match, like so:
<xsl:variable name="foo" select="."/>
This defines a variable $foo of type nodeset. Then access it like this
<xsl:for-each select="$foo/campaigns/campaign">
...
</xsl:for-each>
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>