Need to set Global variable - xslt

I am using xslt1.0.I want to use a global variable such a way that, the value of variable is set from one template and used in another template..How can i achieve this.
Please help me..Thanks in advance..

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="vTest">
<xsl:apply-templates select="/*/element"/>
</xsl:variable>
<xsl:template match="/">
<xsl:value-of select="$vTest"/>
</xsl:template>
</xsl:stylesheet>
XML input:
<t>
<element>A</element>
<element>B</element>
<element>C</element>
</t>
Result:
ABC
Note: $vTest with a value template will be RTF in 1.0. But for this case it can be used like as of string data-type.

Related

XSLT dynamic Sum based on a parameter

I am calling this XSL from Apache Camel and I set the header of the message to the parameter, but still I don't get the result.
I want an XSLT which gives me the sum based on a parameter the problem is that the parameter could be multiple values or one value
<EmpJob>
<EmpJob>
<userId>testID</userId>
<EmpPayCompRecurring>
<payComponent>1010</payComponent>
<endDate>2020-06-30T00:00:00.000</endDate>
<paycompvalue>3025.67</paycompvalue>
<userId>testID</userId>
<currencyCode>EUR</currencyCode>
<startDate>2020-06-01T00:00:00.000</startDate>
</EmpPayCompRecurring>
<EmpPayCompRecurring>
<payComponent>6097</payComponent>
<endDate>2019-12-31T00:00:00.000</endDate>
<paycompvalue>100.0</paycompvalue>
<userId>testID</userId>
<currencyCode>EUR</currencyCode>
<startDate>2018-12-06T00:00:00.000</startDate>
</EmpPayCompRecurring>
</EmpJob>
</EmpJob>
I created an XSLT transformation
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name = "custReturnDate" />
<xsl:template match="/EmpJob">
<xsl:variable name="apos">'</xsl:variable>
<xsl:variable name="payrecur">'1010','6097' </xsl:variable>
<mainroot>
<xsl:for-each select="EmpJob">
<root>
<__metadata>
<uri><xsl:value-of select="concat('EmpCompensation(seqNumber=1L,startDate=datetime',$apos ,$custReturnDate, $apos,',userId=',$apos,userId,$apos,')')"/></uri>
</__metadata>
<customDouble13><xsl:value-of select="sum(EmpPayCompRecurring[$payrecur]/paycompvalue) "/></customDouble13>
</root>
</xsl:for-each>
</mainroot>
</xsl:template>
</xsl:stylesheet>
"payrecur" should be the parameter later on according to this parameter I should sum node values.
P.S. I can change the parameter to anything because I am calling the template from code.
Given XSLT 2 or 3, I would declare the parameter as a sequence of strings or integers and then compare sum(EmpPayCompRecurring[payComponent = $payrecur]/paycompvalue).
So declare e.g <xsl:param name="payrecur" as="xs:string*" select="'1010','6097'"/>.
A minimal stylesheet would be
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:param name = "custReturnDate" />
<xsl:param name="payrecur" as="xs:string*" select="'1010','6097'"/>
<xsl:output indent="yes"/>
<xsl:template match="/EmpJob">
<xsl:variable name="apos">'</xsl:variable>
<mainroot>
<xsl:for-each select="EmpJob">
<root>
<__metadata>
<uri><xsl:value-of select="concat('EmpCompensation(seqNumber=1L,startDate=datetime',$apos ,$custReturnDate, $apos,',userId=',$apos,userId,$apos,')')"/></uri>
</__metadata>
<customDouble13>
<xsl:value-of select="sum(EmpPayCompRecurring[payComponent = $payrecur]/paycompvalue)"/>
</customDouble13>
</root>
</xsl:for-each>
</mainroot>
</xsl:template>
</xsl:stylesheet>
At https://xsltfiddle.liberty-development.net/bEzkTcM I get <customDouble13>3125.67</customDouble13>. You will probably want to add exclude-result-prefixes="#all" on the xsl:stylesheet element.
Hello everyone in case anyone faces this type of scenario as I mentiond before I added the parameter in the header so that the XSLT template will get it. Unfortunatly the template received it as a String so I had to use the function tokenize() to convert the parameter to array then I could use the template provided by Martin Honnen.
This scenario will work Apache Camel as well as SAP CPI
Best Regards
Ibrahim

How to create template to match based upon an XSLT parameter

I'm trying to create a standard-use XSLT that will perform a given task based upon a user-provided XPATH expression as an XSLT parameter.
That is, I need something like this:
<xsl:template match="$paramContainingXPATH">
<!-- perform the task on the node(s) in the given xpath -->
</xsl:template>
For example, suppose I have some XML:
<xml>
<nodeA>whatever</nodeA>
<nodeB>whatever</nodeB>
<nodeC>whatever</nodeC>
<nodeD>whatever</nodeD>
<nodeE>whatever</nodeE>
</xml>
The XSLT needs to transform just a node or nodes matching a provided XPATH expression. So, if the xslt parameter is "/xml/nodeC", it processes nodeC. If the xslt parameter is "*[local-name() = 'nodeC' or local-name() = 'nodeE']", it processes nodeC and nodeE.
This should work for absolutely any XML message. That is, the XSLT cannot have any direct knowledge of the content of the XML. So, it could be a raw XML, or a SOAP Envelope.
I was guessing I might need to grab all the nodes matching the xpath, and then looping over them calling a named template, and using the standard identity template for all other nodes.
All advice is appreciated.
If you really need that feature with XSLT 1.0 or 2.0 then I think you should consider writing one stylesheet that takes that string parameter with the XPath expression and then simply generates the code of a second stylesheet where the XPath expression is used as a match pattern and the other needed templates like the identity template are included statically. Dynamic XPath evaluation is only available in XSLT 3.0 or in earlier versions as a proprietary extension mechanism.
You cannot match a template using a parameter - but you can traverse the tree and compare the path of each node with the given path. Here's a simple example:
XSLT 1.0
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="path" select="'/world/America/USA/California'"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="*"/>
</root>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="path-to-me">
<xsl:for-each select="ancestor-or-self::node()">
<xsl:value-of select="name()" />
<xsl:if test="position()!=last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:if test="$path=$path-to-me">
<xsl:call-template name="action"/>
</xsl:if>
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template name="action">
<return>
<xsl:value-of select="." />
</return>
</xsl:template>
</xsl:stylesheet>
Applied to a slightly more ambitious test input of:
<world>
<Europe>
<Germany>1</Germany>
<France>2</France>
<Italy>3</Italy>
</Europe>
<America>
<USA>
<NewYork>4</NewYork>
<California>5</California>
</USA>
<Canada>6</Canada>
</America>
</world>
the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<return>5</return>
</root>
This could be made more efficient by passing the accumulated path as a parameter of the recursive template, so that each node needs only to add its own name to the chain.
Note:
The given path must be absolute;
Predicates (including positional predicates) and attributes are not implemented in this. They probably could be, with a bit more effort;
Namespaces are ignored (I don't see how you could pass an XPath as a parameter and include namespaces anyway).
If your processor supports an evaluate() extension function, you could forgo the calculated text path and test for intersection instead.
Edit:
Here's an example using EXSLT dyn:evaluate() and set:intersection():
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dyn="http://exslt.org/dynamic"
xmlns:set="http://exslt.org/sets"
extension-element-prefixes="dyn set">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="path" select="'/world/America/USA/California'"/>
<xsl:variable name="path-set" select="dyn:evaluate($path)" />
<xsl:template match="/">
<root>
<xsl:apply-templates select="*"/>
</root>
</xsl:template>
<xsl:template match="*">
<xsl:if test="set:intersection(. , $path-set)">
<xsl:call-template name="action"/>
</xsl:if>
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template name="action">
<return>
<xsl:value-of select="." />
</return>
</xsl:template>
</xsl:stylesheet>
Note that this will also work with with paths like:
/world/America/USA/*[2]
//California
and many others that the text comparison method could not accommodate.
I'm sending the element name as a param to the XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:output method="xml"/>
<xsl:param name="user"/>
<xsl:template match="/">
<xsl:call-template name="generic" />
</xsl:template>
<xsl:template name="generic">
<count><xsl:value-of select="count(.//*[local-name()=$user])"/></count>
</xsl:template>
</xsl:stylesheet>
I hope this could help!

Converting XML format using XSLT

My requirement is to convert the following array
<array>
<value>755</value>
<value>5861</value>
<value>4328</value>
</array>
to this array.
<array>
<int>755</int>
<int>5861</int>
<int>4328</int>
</array>
Following is my XSLT code to do the transformation & it works. Is it the correct way because in one post I saw the use of "identity template". but I haven't used it.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/array">
<xsl:element name="array">
<xsl:for-each select="value">
<xsl:element name="int">
<xsl:value-of select="." />
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Your current method works. It is more of a "pull" style stylesheet. The "push" style uses apply-templates.
You could shorten it a bit by using element literals, which makes it a little easier to read:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/array">
<array>
<xsl:for-each select="value">
<int>
<xsl:value-of select="." />
</int>
</xsl:for-each>
</array>
</xsl:template>
</xsl:stylesheet>
A solution using the identity template and a custom template for the value element:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<!--identity template, which copies every attribute and
node(element, text, comment, and processing instruction)
that it matches and then applies templates to all of it's
attributes and child nodes (if there are any) -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--Specialized template that matches the value element.
Because it is more specific than the identity template above it has
a higher priority and will match when the value element is encountered.
It creates an int element and then applies templates to any attributes
and child nodes of the value element -->
<xsl:template match="value">
<int>
<xsl:apply-templates select="#*|node()"/>
</int>
</xsl:template>
</xsl:stylesheet>
You can do:
<xsl:template match="/array">
<array>
<xsl:for-each select="value">
<int>
<xsl:value-of select="." />
</int>
</xsl:for-each>
</array>
</xsl:template>
Here is a quote from the accepted answer of your link:
XSL cannot replace anything. The best you can do is to copy the parts you want to keep, then output the parts you want to change instead of the parts you don't want to keep.
That is where the identity template comes into play: it copies everything not targetted by another matching template. The upshot is, that if your base XML contains other content than just the array, then you should also include the identity template in your xslt. But if you are sure that your xml will contain no other content, then you don't need it.

Can I check condition in template match in XSLT?

I want to check variable in template match, is it possible?
like:
<xsl:template match="*:Item and $MODE='PURCHASE'">
So template should check variable $MODE='PURCHASE' as well
Not in XSLT 1.0.
In XSLT 2.0 one can have variable references -- in the predicates of the template match pattern.
For example:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="MODE" select="'PURCHASE'"/>
<xsl:template match="*:Item[$MODE='PURCHASE']">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on this XML document:
<t xmlns:x="some:x">
<x:Item>someText</x:Item>
</t>
the wanted, correct result is produced:
someText

How can I select nodes from a tree whose markup is stored in a variable?

Consider the following XSLT script:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="iso-8859-1"/>
<xsl:variable name="stringmap">
<map>
<entry><key>red</key><value>rot</value></entry>
<entry><key>green</key><value>gruen</value></entry>
<entry><key>blue</key><value>blau</value></entry>
</map>
</xsl:variable>
<xsl:template match="/">
<!-- IMPLEMENT ME -->
</xsl:template>
</xsl:stylesheet>
I'd like this script to print redgreenblue.
Is there any way to treat the XML markup which is stored in the stringmap variable as a document of its own which I can run XPath queries on? I'm basically looking for something like
<xsl:for-each select="document($stringmap)/map/entry">
<xsl:value-of select="key"/>
</xsl:for-each>
(except that the document() function expects an URI).
Motivation: I have various long <xsl:choose> elements which map a given string to another string. I'd like to replace all those with a single template which takes a 'map' argument (which is a simple XML document). My hope is that I can then replace the <xsl:choose> with a simple statement like <xsl:value-of select="$stringmap/map/entry/value[../key='$givenkey']"/>
I'm using XSLT 1.0 using xsltproc.
You're almost right, using document('') will allow you to process node sets inside the current stylesheet:
<xsl:for-each select="document('')/xsl:stylesheet/xsl:variable[#name='stringmap']/map/entry">
<xsl:value-of select="key"/>
</xsl:for-each>
It's not necessary to define the map node set as a variable in this case:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:data="some.uri" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<data:map>
<entry><key>red</key><value>rot</value></entry>
<entry><key>green</key><value>gruen</value></entry>
<entry><key>blue</key><value>blau</value></entry>
</data:map>
<xsl:template match="/">
<xsl:for-each select="document('')/xsl:stylesheet/data:map/entry">
<xsl:value-of select="key"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
If you do not use xsl:variable as a wrapper, you must remember that a top level elements must have a non null namespace URI.
In XSLT 2.0 it would've been possible to just iterate over the content in a variable:
<xsl:variable name="map">
<entry><key>red</key><value>rot</value></entry>
<entry><key>green</key><value>gruen</value></entry>
<entry><key>blue</key><value>blau</value></entry>
</xsl:variable>
<xsl:template match="/">
<xsl:for-each select="$map/entry">
<xsl:value-of select="key"/>
</xsl:for-each>
</xsl:template>
A posting by M. David Peterson just taught me how to make this work:
It's not necessary to have an <xsl:variable> for this case. Instead, I can embed the data document directly into the XSL stylesheet (putting it into a namespace for sanity) and then select elements from that. Here's the result:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="uri:map">
<xsl:output method="text" encoding="iso-8859-1"/>
<map:colors>
<entry><key>red</key><value>rot</value></entry>
<entry><key>green</key><value>gruen</value></entry>
<entry><key>blue</key><value>blau</value></entry>
</map:colors>
<xsl:template match="/">
<xsl:for-each select="document('')/*/map:colors/entry">
<xsl:value-of select="key"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This generates the expected output redgreenblue.
The trick is to use document('') to get a handle to the XSLT document itself, then * to get into the toplevel xsl:stylesheet element and from there I can access the color map.