How to call template from another template in xslt 1.0 - xslt

I have two templates like this ...one to create normal package name and another to append "-Non" with the name if it passes certain conditions as shown
<xsl:template match="text()" name="checkForAgency">
<xsl:param name="Name"/>
<xsl:param name="Agency"/>
<xsl:if test="contains($Agency,'AG') ">
<xsl:value-of select="concat($Name,' - Non')"/>
</xsl:if>
</xsl:template>
<xsl:template match="text()" name="createPackageName">
<xsl:param name="Type"/>
<xsl:if test="contains($Type,'NEW')">
<xsl:value-of select="concat('New',' Goal')"/>
</xsl:if>
<xsl:if test="contains($Type,'AMD')">
<xsl:value-of select="concat('Amended',' Goal')"/>
</xsl:if>
<xsl:if test="contains($Type,'REN') or contains($Type,'ARN')">
<xsl:value-of select="concat('Renewal',' Goal')"/>
</xsl:if>
</xsl:template>
Calling these templates from
<xsl:variable name="NonAgencyPackageName">
<xsl:call-template name="createPackageName">
<xsl:with-param name="Type" select="ID"/>
</xsl:call-template>
</xsl:variable>
<xsl:element name="PackageName">
<xsl:call-template name="checkForAgency">
<xsl:with-param name="Name" select="$NonAgencyPackageName"/>
<xsl:with-param name="Agency" select="Output"/>
</xsl:call-template>
</xsl:element>
If ID has 'New', the package name should be New Goal and if the Output has 'AG', then name should be New Goal- Non.
Else name should be new goal. I want it two separate templates only. I am getting empty value now. Please help me in achieving this and how to correct it?

At a guess, the XPaths ID and Output are not selecting what you expect, so all conditionals are failing. Since you don't provide any XML input, it's impossible to be sure.
Ask yourself several questions:
When this code is evaluated, what is the current element?
Does this element have a child element named ID?
What is the string value of that element?
Does it have a child element named Output?
What is its string value?
The PackageName element you generate will be non-empty only if there is an Output child and its string value contains the sequence 'AG'. It will contain the string 'New Goal - Non' only if the Output child contains 'AG' and the ID child contains NEW. No power in creation could make it have the value 'New Goal', since the template CheckForAgency either produces a value ending with ' - Non' or no output at all. (You may wish to read about the choose/when instruction in XSLT.)

Related

How to make template call work in xslt 1.0

I have this following template which i am not sure correct or not
<xsl:template match="text()" name="createName">
<xsl:param name="Type"/>
<xsl:if test="contains(Type,'NEW')">
<xsl:value-of select="concat('New','Goal')"/>
</xsl:if>
<xsl:if test="contains(Type,'AMD')">
<xsl:value-of select="concat('Amended','Goal')"/>
</xsl:if>
</xsl:template>
Calling this template here
<xsl:element name="PackageName">
<xsl:call-template name="createName">
<xsl:with-param name="Type" select="s0:PIXField/s0:TransactionID"/>
</xsl:call-template>
</xsl:element>
s0:PIXField/s0:TransactionID = BPA201605311506452806320060A1AMD
I want to create PackageName element with value 'Amended Goal'.
But for now i am getting empty PackageName. dont know where my code fails.
Please help.
If you want to access a parameter, you need to use the $ prefix. That is to say, do contains($Type,'NEW') and not contains(Type,'NEW'). The latter is looking for a child element called Type, and not the passed in parameter
Try this template. Note that you don't really need to use xsl:value-of with concat to output the text.
<xsl:template match="text()" name="createName">
<xsl:param name="Type"/>
<xsl:if test="contains($Type,'NEW')">
<xsl:text>New Goal</xsl:text>
</xsl:if>
<xsl:if test="contains($Type,'AMD')">
<xsl:text>Amended Goal</xsl:text>
</xsl:if>
</xsl:template>
Also note, you may not actually need the match attribute on the template either, if the template is only ever being called as a named template.
Also consider using xsl:choose / xsl:when instead of xsl:if.
You call this template in exactly the same way as shown in your question
<xsl:element name="PackageName">
<xsl:call-template name="createName">
<xsl:with-param name="Type" select="s0:PIXField/s0:TransactionID"/>
</xsl:call-template>
</xsl:element>
Nothing changes there at all.

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.

Usage of dollar "$" in xsl

In the following piece of code, I want to understand what is "$type" here and how it is being used.
How this if condition is being applied using "$type".
<xsl:template name="CodValue">
<xsl:param name="type"/>
<xsl:param name="nodeNM">category</xsl:param>
<xsl:element name="{$nodeNM}">
<xsl:if test="$type">
<xsl:attribute name="xsi:type">
<xsl:value-of select="$type"/>
</xsl:attribute>
</xsl:if>
</xsl:element>
</xsl:template>
For this template, you need a parameter, another one is optional nodeNM. You can call the parameter like this:
<xsl:call-template name="CodValue">
<xsl:with-param name="type" select="123" />
</xsl:call-template>
or
<xsl:call-template name="CodValue">
<xsl:with-param name="type">123</xsl:with-param>
</xsl:call-template>
type is a variable in CodValue, so you can print it via
<xsl:value-of select="$type" />
or via {$type} in attributes.
Suggestions:
$nodeNM looks like the name of a tag (html-tags if you produce HTML-Code).
$type (if returns true from xpath, ie. if not empty) will create a xsi:type-Tag-Attribute.
So if you call
<xsl:call-template name="CodValue">
<xsl:with-param name="type" select="123" />
</xsl:call-template>
Your XML will be transformed into
<category xsi:type="123" />
$ is used to reference a variable inside an XPath expression.
In this particular case, $type is declared earlier by <xsl:param name="type"/>. However, it has not been given a value so you will need to use <xsl:with-param> when calling the template to so that you can provide a value.
n.b. The variable $nodeNM was given a default value, so you don't need to specify that when calling the template.

How to mimic calling template-match with a parameter value?

I'm looking for a workaround on passing parameters to a template-match. I'm aware this isn't allowed within XPath, and therefore I'm looking for a 'plan B' solution.
This is what I wished would work :
Part 1 of xslt (2.0) :
<xsl:template match="/">
<xsl:for-each select="//Main/PageList/Page">
<xsl:result-document href="{#ID}.xml">
<Page ID="{#ID}">
<xsl:apply-templates select="node()">
<xsl:with-param name="theID" select="#ID"/>
</xsl:apply-templates>
</Page>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
So fairly straightforward, don't bother about the tags used, what it does in essence is go through a node, and for each node it creates a single XML file. Each node starts with an ID, and it's this ID I'd like to make available for other templates. Unfortunately this works fine for named templates, but it doesn't work for matched ones (if I understood the theory correctly at least)
So below is what I'd like to see working :
<!-- identity template -->
<xsl:template match="node()|#*">
<xsl:param name="theID"/>
<xsl:copy>
<xsl:apply-templates select="node()|#*">
<xsl:with-param name="theID" select="$theID"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<!-- lots of templates here doing what I want them to do -->
<!-- one template creating an issue -->
<xsl:template match="#src">
<!-- would be nice to know the current ID, but unfortunately this one stays empty... -->
<xsl:param name="theID"/>
<!-- clean current attribute a bit -->
<xsl:variable name="S1" select="replace(.,'\.\.\/','')"/>
<xsl:attribute name="src">
<xsl:choose>
<xsl:when test="contains($S1,'common')">
<!-- just use current value, don't bother about current ID -->
<xsl:value-of select="$S1"/>
</xsl:when>
<xsl:otherwise>
<!-- use ID parameter -->
<xsl:value-of select="concat($theID,'_',$S1)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:template>
Don't look at the code as such, it's just a smaller part of the whole file, but the essence is that I want to use the ID parameter from my first part (match="/") inside the other template (match="#src"), but this seems to be rather complex.
Am I missing something ? If I'm just having bad luck and it's not possible indeed, would anyone have an advice how I could proceed ?
Thanks in advance !
Your problem is here:
<xsl:apply-templates select="node()">
<xsl:with-param name="theID" select="#ID"/>
</xsl:apply-templates>
the select attribute specifies that the processing should continue on any children nodes of the current node.
However node() only selects children nodes (elements, text nodes, processing instructions and comments) -- not attributes.
Solution:
In order to directly cause processing of (all) attributes of the current node, use:
--
<xsl:apply-templates select="#*">
<xsl:with-param name="theID" select="#ID"/>
</xsl:apply-templates>
.2. In order to cause the processing only of the src attribute of the current node use:
--
<xsl:apply-templates select="#src">
<xsl:with-param name="theID" select="#ID"/>
</xsl:apply-templates>
.3. In order to process indirectly attributes tof the descendants of the current node, do:
--
<xsl:apply-templates select="node()|#*">
<xsl:with-param name="theID" select="#ID"/>
</xsl:apply-templates>
You also must ensure that any template that processes node() must have an xsl:param named theID and that it must pass this param in any xsl:apply-templates instruction.
In practice this means that you mus override all XSLT built-in templates, because they aren't aware of your xsl:param.
I think instead of
<xsl:apply-templates select="node()">
<xsl:with-param name="theID" select="#ID"/>
</xsl:apply-templates>
you rather want
<xsl:apply-templates select="node()">
<xsl:with-param name="theID" select="parent::Page/#ID"/>
</xsl:apply-templates>
assuming you want to pass on the ID attribute of the parent Page element.
On the other hand the select="node()" select any child nodes like child elements, child comment nodes, child text nodes, child processing instruction nodes, so I don't see why you later show a template matching an attribute node.

Using xsl:variable in a xsl:foreach select statement

I'm trying to iterate through an xml document using xsl:foreach but I need the select=" " to be dynamic so I'm using a variable as the source. Here's what I've tried:
...
<xsl:template name="SetDataPath">
<xsl:param name="Type" />
<xsl:variable name="Path_1">/Rating/Path1/*</xsl:variable>
<xsl:variable name="Path_2">/Rating/Path2/*</xsl:variable>
<xsl:if test="$Type='1'">
<xsl:value-of select="$Path_1"/>
</xsl:if>
<xsl:if test="$Type='2'">
<xsl:value-of select="$Path_2"/>
</xsl:if>
<xsl:template>
...
<!-- Set Data Path according to Type -->
<xsl:variable name="DataPath">
<xsl:call-template name="SetDataPath">
<xsl:with-param name="Type" select="/Rating/Type" />
</xsl:call-template>
</xsl:variable>
...
<xsl:for-each select="$DataPath">
...
The foreach threw an error stating: "XslTransformException - To use a result tree fragment in a path expression, first convert it to a node-set using the msxsl:node-set() function."
When I use the msxsl:node-set() function though, my results are blank.
I'm aware that I'm setting $DataPath to a string, but shouldn't the node-set() function be creating a node set from it? Am I missing something? When I don't use a variable:
<xsl:for-each select="/Rating/Path1/*">
I get the proper results.
Here's the XML data file I'm using:
<Rating>
<Type>1</Type>
<Path1>
<sarah>
<dob>1-3-86</dob>
<user>Sarah</user>
</sarah>
<joe>
<dob>11-12-85</dob>
<user>Joe</user>
</joe>
</Path1>
<Path2>
<jeff>
<dob>11-3-84</dob>
<user>Jeff</user>
</jeff>
<shawn>
<dob>3-5-81</dob>
<user>Shawn</user>
</shawn>
</Path2>
</Rating>
My question is simple, how do you run a foreach on 2 different paths?
Try this:
<xsl:for-each select="/Rating[Type='1']/Path1/*
|
/Rating[Type='2']/Path2/*">
Standard XSLT 1.0 does not support dynamic evaluation of xpaths. However, you can achieve your desired result by restructuring your solution to invoke a named template, passing the node set you want to process as a parameter:
<xsl:variable name="Type" select="/Rating/Type"/>
<xsl:choose>
<xsl:when test="$Type='1'">
<xsl:call-template name="DoStuff">
<xsl:with-param name="Input" select="/Rating/Path1/*"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$Type='2'">
<xsl:call-template name="DoStuff">
<xsl:with-param name="Input" select="/Rating/Path2/*"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
...
<xsl:template name="DoStuff">
<xsl:param name="Input"/>
<xsl:for-each select="$Input">
<!-- Do stuff with input -->
</xsl:for-each>
</xsl:template>
The node-set() function you mention can convert result tree fragments into node-sets, that's correct. But: Your XSLT does not produce a result tree fragment.
Your template SetDataPath produces a string, which is then stored into your variable $DataPath. When you do <xsl:for-each select="$DataPath">, the XSLT processor chokes on the fact that DataPath does not contain a node-set, but a string.
Your entire stylesheet seems to be revolve around the idea of dynamically selecting/evaluating XPath expressions. Drop that thought, it is neither possible nor necessary.
Show your XML input and specify the transformation your want to do and I can try to show you a way to do it.