My xsl has a parameter
<xsl:param name="halfPath" select="'halfPath'"/>
I want to use it inside match
<xsl:template match="Element[#at1='value1' and not(#at2='{$halfPath}/another/half/of/the/path')]"/>
But this doesn't work. I guess a can not use parameters inside ''. How to fix/workaround that?
The XSLT 1.0 W3C Specification forbids referencing variables/parameters inside a match pattern.:
"It is an error for the value of the
match attribute to contain a
VariableReference"
There is no such limitation in XSLT 2.0, so use XSLT 2.0.
If due to unsurmountable reasons using XSLT2.0 isn't possible, put the complete body of the <xsl:template> instruction inside an <xsl:if> where the test in conjunction with the match pattern is equivalent to the XSLT 2.0 match pattern that contains the variable/parameter reference(s).
In a more complicated case where you have more than one template matching the same kind of node but with different predicates that reference variables/parameters, then a wrapping <xsl:choose> will need to be used instead of a wrapping <xsl:if>.
Well, you could use a conditional instruction inside the template:
<xsl:template match="Element[#at1='value1']">
<xsl:if test="not(#at2=concat($halfPath,'/another/half/of/the/path'))">
.. do something
</xsl:if>
</xsl:template>
You just need to be aware that this template will handle all elements that satisfy the first condition. If you have a different template that handles elements that match the first, but not the second, then use an <xsl:choose>, and put the other template's body in the <xsl:otherwise> block.
Or, XSLT2 can handle it as is if you can switch to an XSLT2 processor.
This topic had the answer to my question, but the proposed solution by Flynn1179 was not quite correct for me (YMMV). So try it the way it is suggested by people more expert than me, but if it doesn't work for you, consider how I solved it. I am using xsltproc that only handles XSL version 1.0.
I needed to match <leadTime hour="0024">, but use a param: <xsl:param name="hour">0024</xsl:param>. I found that:
<xsl:if test="#hour='{$hour}'"> did not work, despite statements here and elsewhere that this is the required syntax for XSL v.1.0.
Instead, the simpler <xsl:if test="#hour=$hour"> did the job.
One other point: it is suggested above by Dimitre that you put template inside if statement. xsltproc complained about this: instead I put the if statement inside the template:
<xsl:template match="leadTime">
<xsl:if test="#hour=$leadhour">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:if>
</xsl:template>
In XSLT 2.0 you can refer to global variables within a match pattern, but the syntax is simpler than your guess:
<xsl:template match="Element[#at1='value1' and
not(#at2=$halfPath/another/half/of/the/path)]"/>
rather than
<xsl:template match="Element[#at1='value1' and
not(#at2='{$halfPath}/another/half/of/the/path')]"/>
Also, the semantics are not what you appear to be expecting: a variable referenced on the lhs of "/" must contain a node-set, not a fragment of an XPath expression.
Related
Depending on a specific parameter value, I need to either omit matched elements from a transformed document or allow the matched elements to be transformed according to rules in other matching templates. I have a complex xml doc with dozens of element/attribute types, all handled in a huge variety of ways in dozens of templates. If any element has the attribute value omit_attr="true" and the stylesheet has the parameter omit_param="true", I need to omit them from the transformed document. If however my parameter is omit_param="false" then I need to apply whatever other rules exist for my omit_attr="true" elements. Here's my unacceptable template:
<xsl:template match="*[#omit_attr= 'true']">
<xsl:choose>
<xsl:when test="($omit_param= 'true')"/>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This omits content just fine when omit_param="true". The problem occurs when omit_param="false" and I need to process omit_attr="true" elements with other applicable templates. This code transforms the matched element's children, but not the matched element, which is unacceptable because the matched element will have its own set of transform rules elsewhere. And of course <xsl:apply-templates select="."/> attempts to process the matched element as desired, but creates an endless loop in the process.
So, how can I test for omit_attr="true" in all possible elements, and still apply the alternate templates to omit_attr="true" elements when omit_param="false"?
If I could wrap my xsl:template in if big <if test="omit_param='true'> statement, that would do the job, but of course it's against the rules...
If that is an XSLT 2 or later processor you can just use
<xsl:template match="*[#omit_attr= 'true' and $omit_param= 'true']"/>
to block the element from being processed/copied/transformed if the two conditions hold and have your other templates applied if not. For XSLT 1 processors I am not sure they allow a variable reference in a match pattern.
Within an xsl:for-each select loop, I have <xsl:number count="//headline"/> that correctly gives me the node #; Now I want to use that number in an xsl:if test block, but I cannot get the test expression right, msxml4.dll keeps kicking back errors. Am using xsl 1.0 (and stuck with it for now)
So, in <xsl:if test="expression">...output if the expression is true..</xsl:if>
I want the test expression to essentially be like this (so I can do something specific for Node #4, in this example):
<xsl:number count="//headline"/> = 4
This is what I have that does not work:
<xsl:if test="<xsl:number count="//headline"/> = 4">
Thanks in advance for any insights,
George
As #michael.hor257k explains, the general approach is to put the xsl:number call inside an xsl:variable (or in XSLT 2.0, inside an xsl:function). Sometimes though it's more convenient to abandon xsl:number:
<xsl:if test="count(preceding::headline) = 3">...</xsl:if>
If it's a big document then both xsl:number and preceding::headline are potentially expensive when executed inside a loop, and if this is a concern then you should compare them under your chosen XSLT processor: one may be optimized better than the other.
Come to think of it, your use of xsl:number looks odd to me. The count attribute is a pattern, and the pattern //headline matches exactly the same nodes as the pattern headline. As a result I misread your call on xsl:number as counting all the headlines in the document, whereas it actually only counts the preceding-sibling headlines. I wonder if that is what you intended?
If (!) I understand correctly, you want to do something like:
<xsl:variable name="n">
<xsl:number count="headline"/>
</xsl:variable>
<xsl:value-of select="$n"/>
<xsl:if test="$n = 4">
<!-- do something -->
</xsl:if>
We are using Xalan XSLT 1.0 in Java and we want to pass a variable to a template match to avoid hard-coding element names in the XSL file. The style sheet compiles, but the date returned is wrong. Are we using the correct syntax?
Possible XML inputs...
<books>
<book/>
<book/>
</books>
<dvds>
<dvd/>
<dvd/>
</dvds>
<xsl:variable name="matchElement" select="'book'"/>
<!-- OR -->
<xsl:variable name="matchElement" select="'dvd'"/>
<xsl:template match="/*[local-name() = $matchElement]">
This xsl:template:
<xsl:template match="/*[local-name() = $matchElement]">
is matching from root.
Either remove the / from /* or change it to //* (depending on how the rest of your stylesheet is designed).
Also, if you use xsl:param instead of xsl:variable, you can set the value from the command line.
Your variable syntax is correct, but note that it is technically illegal to use variable or parameter references in XSLT 1.0 match patterns. It is possible, however, that Xalan has implemented this behavior outside of the standard. (#DevNull's comment about your expression also applies.)
Suppose I have XML like this:
<xsl:template match="assessment | section ">
.
.
.
</xsl:template>
I do this because I mostly want to treat assessment and section nodes the same. However, I do want to treat them a little differently. How can I tell whether a match was for assessment or for section?
Do something like this:
<xsl:if test="name()='section'">
</xsl:if>
You may test for:
self::assessment
which is slightly more efficient than using the name() function.
However in cases like this I will put the common code in another template (either named or in a named mode) and will instantiate the common processing like this:
<xsl:apply-templates select="." mode="common"/>
What do I need to do to this:
<xsl:template match="xs:simpleType">
<xsl:copy>
<xsl:copy-of select="node()[not(self::xs:annotation or self::xs:restriction)]|#*"/>
</xsl:copy>
</xsl:template>
Currently, this turns out:
<xs:simpleType xmlns:core="urn:org:pesc:core:CoreMain:v1.4.0" name="SINIDType">
</xs:simpleType>
I'd prefer it to look like:
<xs:simpleType name="SINIDType" />
with those blank lines in there, it looks like your select statement is (correctly) selecting your whitespace nodes as well as your elements. try using
select="*[not(self::xs:annotation or self::xs:restriction)]|#*"
which will only match element nodes, not text nodes.
The serializer is responsible for whether an empty element is emitted as <abc></abc> or <abc/>, and they are exactly equivalent. Some serializers give no option, and will always produce one or the other.
But it might be that you are emitting whitespace between them; in that case you'll have to change the xsl:copy to something else that doesn't include the insignificant whitespace, like by adding or text() = '' to your predicate.
As far as eliminating the xmlns:core namespace declaration, that depends on your context. It will be generated always if there is an element that requires it inside of your type, or if you're using XSLT and haven't excluded the namespace with the #exclude-result-prefixes attribute on the <xsl:stylesheet> root element. And even then, depending on your processor environment, the serializer may "decide" that it wants that namespace output needlessly, since it was in scope in the input.
Also, it's kind of odd to see <xsl:copy> ... <xsl:copy-of .../> ... </xsl:copy>. You really shouldn't wrap the copy-of inside of the copys. Just make it copy-of.