xslt apply-templates on node-set - xslt

I have to use XSL 1.0 and I have an xslt variable containing a result tree fragment. I want to apply template styles to that variable.
<xsl:template match="item">
<p>
<xsl:apply-templates />
</p>
</xsl:template>
<xsl:template match="html_embed">
<xsl:variable name="htmlContents">
<item>hello ian</item>
<item>how are you?</item>
</xsl:variable>
<xsl:choose>
<xsl:when test="function-available('msxsl:node-set')">
<xsl:apply-templates select="msxsl:node-set($htmlContents)/node()" />
</xsl:when>
<xsl:otherwise>
<p>node set not available</p>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I'm using the node-set function to turn the tree fragment into a node-set but it either isn't working or it can't find a template match because all that's ever returned is this text, on one line, without the markup:
hello ianhow are you?
Any idea's on how I can transform the item nodes?

Related

how to do a dynamic boolean checking in xslt

From my root template for unique account value, call goes to trans template were in the input xml I will have multiple nodes, my requirement is that once the call goes from root template to trans template, if a match of accountId found in between multiple elements, account details template is called only once, irrespective of other match found. I need a solution for above requirement.
input sample :
<elements><accountId>1</accountId></elements>
<elements><accountId>1</accountId></elements>
<elements><accountId>2</accountId></elements>
<elements><accountId>2</accountId></elements>
<elements><accountId>3</accountId></elements>
The below line should be wrapped under some code so its called only once
<xsl:call-template name="Account_details" />
Below is my complete code for xsl
<xsl:template match="/">
<xsl:variable name="unique-accounts" select="//*/*/*/accountId/text()[generate-id()=generate-id(key('account-by-id', .)[1])]"/>
<xsl:for-each select="$unique-accounts">
<xsl:variable name="currentValue" select="current()"/>
<xsl:apply-templates select="//trans">
<xsl:with-param name="passCurrentValue" select="$currentValue"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="trans">
<xsl:param name="passCurrentValue" />
<xsl:variable name="booleanValue" select="true()"/>
<xsl:for-each select="elements">
<xsl:if test="$passCurrentValue=/*/*/accountId">
<xsl:if test="$booleanValue">
<xsl:call-template name="Account_details" />
<xsl:variable name="booleanValue" select="false()"></xsl:variable>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="Account_details">
.............
</xsl:template>
With the code
<xsl:template match="/">
<xsl:variable name="booleanCheck" select="true"></xsl:variable>
the variable booleanCheck is a node-set (XSLT 1.0) or sequence of elements named true below the document node /. So unless your XML input has a root element named true the value is an empty node-set respectively empty sequence. If you want to select a boolean value then use <xsl:variable name="booleanValue" select="true()"/>. See http://www.w3.org/TR/xpath/#function-true. Then you can test <xsl:if test="$booleanValue">. That should explain how to use boolean values, whether that fits into your context I am not sure.

Structural requirements when using "except" in XPATH/XSL

I am having trouble when using "except" in xpath. Here is the chunk of problem code. (I tried to simplify as much as possible without obscuring the whole problem).:
<!--First, create a variable containing some nodes that we want to filter out.
(I'm gathering elements that are missing child VALUE elements
and whose child DOMAIN and VARIABLE elements only occur once
in the parent list of elements.)
I've confirmed that this part does generate the nodes I want,
but maybe this is the incorrect result structure?-->
<xsl:variable name="badValues">
<xsl:for-each select="$root/A[not(VALUE)]">
<xsl:choose>
<xsl:when test="count($root/A[DOMAIN=current()/DOMAIN and VARIABLE=current()/VARIABLE])=1">
<xsl:copy-of select="."/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<!--Next Loop over the original nodes, minus those bad nodes.
For some reason, this loops over all nodes and does not filter out the bad nodes.-->
<xsl:for-each select="$root/A except $badValues/A"> ...
When you create an xsl:variable without using #select and do not specify the type with the #as, it will create the variable as a temporary tree.
You want to create a sequence of nodes, so that when they are compared in the except operator, they are "seen" as the same nodes. You can do this by specifying as="node()*" for the xsl:variable and by using xsl:sequence instead of xsl:copy-of:
<xsl:variable name="badValues" as="node()*">
<xsl:for-each select="$root/A[not(VALUE)]">
<xsl:choose>
<xsl:when test="count($root/A[DOMAIN=current()/DOMAIN
and VARIABLE=current()/VARIABLE])=1">
<xsl:sequence select="."/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
Alternatively, if you were to use a #select and eliminate the xsl:for-each it would also work. As Martin Honnen suggested, you could use an xsl:key and select the values like this:
<xsl:key name="by-dom-and-var" match="A" use="concat(DOMAIN, '|', VARIABLE)"/>
Then change your badValues to this:
<xsl:variable name="badValues"
select="$root/A[not(VALUE)]
[count(key('by-dom-and-var',
concat(DOMAIN, '|', VARIABLE))/VARIABLE) = 1]"/>>
You can see the difference in the identity of the nodes by using the generate-id() function as you iterate over the items by executing this stylesheet:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:variable name="root" select="*" as="item()*"/>
<xsl:variable name="originalBadValues">
<xsl:for-each select="$root/A[not(VALUE)]">
<xsl:choose>
<xsl:when test="count($root/A[DOMAIN=current()/DOMAIN
and VARIABLE=current()/VARIABLE])=1">
<xsl:copy-of select="."/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="badValues" as="node()*">
<xsl:for-each select="$root/A[not(VALUE)]">
<xsl:choose>
<xsl:when test="count($root/A[DOMAIN=current()/DOMAIN
and VARIABLE=current()/VARIABLE])=1">
<xsl:sequence select="."/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<!--These are the generated ID values of all the A elements-->
<rootA>
<xsl:value-of select="$root/A/generate-id()"
separator=", "/>
</rootA>
<!--These are the generated ID values for
the original $badValues/A -->
<originalBadValues>
<xsl:value-of select="$originalBadValues/A/generate-id()"
separator=", " />
</originalBadValues>
<!--These are the generated ID values for
the correct selection of $badValues-->
<badValues>
<xsl:value-of select="$badValues/generate-id()"
separator=", " />
</badValues>
<!--The generated ID values for the result of
the except operator filter-->
<final>
<xsl:value-of select="($root/A except $badValues)/generate-id()"
separator=", "/>
</final>
</xsl:template>
</xsl:stylesheet>
Executed against this XML file:
<doc>
<A>
<VALUE>skip me</VALUE>
<DOMAIN>a</DOMAIN>
<VARIABLE>a</VARIABLE>
</A>
<A>
<DOMAIN>a</DOMAIN>
<VARIABLE>a</VARIABLE>
</A>
<A>
<DOMAIN>b</DOMAIN>
<VARIABLE>b</VARIABLE>
</A>
<A>
<DOMAIN>c</DOMAIN>
<VARIABLE>c</VARIABLE>
</A>
<A>
<DOMAIN>a</DOMAIN>
<VARIABLE>a</VARIABLE>
</A>
</doc>
It produces the following output:
<rootA>d1e3, d1e15, d1e24, d1e33, d1e42</rootA>
<originalBadValues>d2e1, d2e9</originalBadValues>
<badValues>d1e24, d1e33</badValues>
<final>d1e3, d1e15, d1e42</final>

XSLT Template mode - xpath evaluation

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" />

xslt 1.0 rewrite values of a node according to a map

While transforming a document, I need to 'look up' certain node contents in a 'map', and write those values.
I inlined my 'map' in the transformation.
<xsl:variable name="inlinedmap">
<kat id="stuff">value</kat>
<!-- ... -->
</xsl:variable>
<xsl:variable name="map" select="document('')/xsl:stylesheet/xsl:variable[#name='inlinedmap']" />
<xsl:template match="/">
<xsl:for-each select="/*/foo">
<!-- 'bar' contents should equal to contents of 'kat' -->
<xsl:variable name="g" select="$map/key[.=bar]"/>
<xsl:choose>
<xsl:when test="$g != ''">
<xsl:value-of select="$g/#id"/>
</xsl:when>
<xsl:otherwise>
ERROR
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
I'm always getting ERROR value.
I can't put map value's into attributes, because they contain letters that get escaped.
How can I make it work?
I think there are a few problems here:
You seem to be looking for key elements in your variable, but they're called kat there (typo?)
You seem to be trying to reference the bar child of the context node inside the loop, but you need to use current() to do that
You should create this map as elements in your own namespace instead of an xsl:variable
Here's a complete example. This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my">
<my:vars>
<kat id="stuff">value</kat>
<!-- ... -->
</my:vars>
<xsl:variable name="map" select="document('')/*/my:vars/*"/>
<xsl:template match="/">
<xsl:for-each select="/*/foo">
<!-- 'bar' contents should equal to contents of 'kat' -->
<xsl:variable name="g" select="$map[.=current()/bar]"/>
<xsl:choose>
<xsl:when test="$g != ''">
<xsl:value-of select="$g/#id"/>
</xsl:when>
<xsl:otherwise>
ERROR
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Applied to this input:
<root>
<foo><bar>value</bar></foo>
<foo><bar>value1</bar></foo>
<foo><bar>value2</bar></foo>
<foo><bar>value3</bar></foo>
</root>
Produces this output (one match):
stuff
ERROR
ERROR
ERROR

How to store the current path in xsl?

I would like to store the path of the current node so I can reused it in an expression in XSLT. Is it possible?
<!-- . into $path? -->
<xsl:value-of select="$path" />
Hi, I would like to store the path of
the current node so I can reused it in
an expression in XSLT. Is it possible?
It is possible for any given node to construct an XPath expression that, when evaluated, selects exactly this node. In fact more than one XPath expression exists that selects the same node.
See this answer for the exact XSLT code that constructs such an XPath expression.
The problem is that this XPath expression cannot be evaluated during the same transformation in XSLT 1.0 or XSLT 2.0, unless the EXSLT extension function dyn:evaluate is used (and very few XSLT 1.0 processors implement dyn:evaluate() ).
What you want can be achieved in an easier way in XSLT using the <xsl:variable> instruction:
<xsl:variable name="theNode" select="."/>
This variable can be referenced anywhere in its scope as $theNode, and can be passed as parameter when applying or calling templates.
No, this is not possible with vanilla XSLT 1.0. There is no easy way to retrieve an XPath expression string for a given node, and there is definitely no way to evaluate a string that looks like XPath as if it was XPath.
There are extensions that support dynamic evaluation of XPath expressions, but these are not compatible with every XSLT processor.
In any case, if you provide more detail around what you are actually trying to do, there might be another way to do it.
As #Dimitre and #Tomalak have point out, I don't think it has some value in the same transformation to obtain a string representing an XPath expression for a given node, and then select the node "parsing" such string. I could see some value in performing those operations in different transformations.
Besides that, this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:for-each select=".|//node()|//#*">
<xsl:variable name="vPath">
<xsl:apply-templates select="." mode="getPath"/>
</xsl:variable>
<xsl:value-of select="concat($vPath,'
')"/>
<xsl:call-template name="select">
<xsl:with-param name="pPath" select="$vPath"/>
</xsl:call-template>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template match="/|node()|#*" mode="getPath" name="getPath">
<xsl:apply-templates select="parent::*" mode="getPath"/>
<xsl:text>/</xsl:text>
<xsl:choose>
<xsl:when test="self::*">
<xsl:value-of select="concat(name(),'[',
count(preceding-sibling::*
[name() =
name(current())]) + 1,
']')"/>
</xsl:when>
<xsl:when test="count(.|../#*)=count(../#*)">
<xsl:value-of select="concat('#',name())"/>
</xsl:when>
<xsl:when test="self::text()">
<xsl:value-of
select="concat('text()[',
count(preceding-sibling::text()) + 1,
']')"/>
</xsl:when>
<xsl:when test="self::comment()">
<xsl:value-of
select="concat('comment()[',
count(preceding-sibling::comment()) + 1,
']')"/>
</xsl:when>
<xsl:when test="self::processing-instruction()">
<xsl:value-of
select="concat('processing-instruction()[',
count(preceding-sibling::
processing-instruction()) + 1,
']')"/>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="select">
<xsl:param name="pPath"/>
<xsl:param name="pContext" select="/"/>
<xsl:param name="pInstruction" select="'value-of'"/>
<xsl:variable name="vPosition"
select="number(
substring-before(
substring-after($pPath,
'['),
']'))"/>
<xsl:variable name="vTest"
select="substring-before(
substring-after($pPath,
'/'),
'[')"/>
<xsl:variable name="vPath" select="substring-after($pPath,']')"/>
<xsl:choose>
<xsl:when test="$vPath">
<xsl:call-template name="select">
<xsl:with-param name="pPath" select="$vPath"/>
<xsl:with-param name="pContext"
select="$pContext/*[name()=$vTest]
[$vPosition]"/>
<xsl:with-param name="pInstruction"
select="$pInstruction"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vContext"
select="$pContext/node()
[self::*[name()=$vTest]|
self::comment()[$vTest='comment()']|
self::text()[$vTest='text()']|
self::processing-instruction()
[$vTest =
'processing-instruction()']]
[$vPosition]|
$pContext[$pPath='/']|
$pContext/#*[name() =
substring($pPath,3)]
[not($vTest)]"/>
<xsl:choose>
<xsl:when test="$pInstruction='value-of'">
<xsl:value-of select="$vContext"/>
</xsl:when>
<xsl:when test="$pInstruction='copy-of'">
<xsl:copy-of select="$vContext"/>
</xsl:when>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
With this input:
<?somePI pseudoAttributes?>
<root>
<!-- This is a comment -->
<node attribute="Value">text</node>
</root>
Output:
/
text
/processing-instruction()[1]
pseudoAttributes
/root[1]
text
/root[1]/comment()[1]
This is a comment
/root[1]/node[1]
text
/root[1]/node[1]/#attribute
Value
/root[1]/node[1]/text()[1]
text