putting the xsl condition in a better way - xslt

Please advise is there any better way to express this below xsl condition or this below one is correct.. we are using xslt 1.0
<xsl:if test="$abcPeriod_first=gfd_Rate">
<xsl:value-of select="'AAA'" />
</xsl:if>
<xsl:value-of select="'BBB'" />

If you want an if...else then I believe your current condition is not sufficient. You could use the following:
<xsl:choose>
<xsl:when test="$abcPeriod_first=gfd_Rate">
<xsl:value-of select="'AAA'" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'BBB'" />
</xsl:otherwise>
</xsl:choose>

Related

'tokenize()' is an unknown XSLT function

I am using Visual Studio 2015. I want to find the smallest number in the Comma separated list using XSLT.
<EndUnitizeDate>2020-07-13T15:01:43</EndUnitizeDate>
<InternalRecNum>12,3,44,55,66</InternalRecNum>
<LaunchNum>0</LaunchNum> <LeadingSts>900</LeadingSts>
I used tokenize for splitting but I am getting 'tokenize()' is an unknown XSLT function Error.
<xsl:variable name="smallValue" select="s0:WMWDATA/s0:WMFWUpload/s0:Receipts/s0:Receipt/s0:InternalRecNum/text()" />
<xsl:variable name="tokenizedLine" select="tokenize($smallValue, ',')" />
<xsl:for-each select="$tokenizedLine">
<xsl:sort select="." order="descending" />
<xsl:if test="position() = last()">
Smallest: <xsl:value-of select="." />
</xsl:if>
</xsl:for-each>
enter image description here
The tokenize() function requires an XSLT 2.0 processor.
Some XSLT 1.0 processors support tokenizing via en extension function. In pure XSLT 1.0, you need to use a recursive named template.
Here's an example of a template that will both tokenize the input AND find the smallest token:
<xsl:template name="min-token">
<xsl:param name="input"/>
<xsl:param name="prev-min"/>
<xsl:param name="delimiter" select="','"/>
<xsl:variable name="token" select="substring-before(concat($input, $delimiter), $delimiter)" />
<xsl:variable name="min">
<xsl:choose>
<xsl:when test="not($prev-min) or $token < $prev-min">
<xsl:value-of select="$token"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$prev-min"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains($input, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="min-token">
<xsl:with-param name="input" select="substring-after($input, $delimiter)"/>
<xsl:with-param name="prev-min" select="$min"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$min"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Demo: https://xsltfiddle.liberty-development.net/aiynfe
If functions like tokenize() are more important to you than using Visual Studio, then this question describes alternatives you could consider:
https://stackoverflow.com/questions/11205268/how-to-use-xslt-2-0-in-visual-studio-2010`

Implement dynamic xpath in XSLT: for-each-group

I am trying to use saxon:evaluate to reduce repeated code in an xsl function.
However anything I try returns an error.
This is a section of the repetitive code.
<!--Select by outputclass and group by attribute-->
<xsl:when test="$from='oclass-attribute'">
<xsl:for-each-group select="$compose//*[contains(#outputclass,$sel)]" group-by="#*[name()=$group]">
<xsl:sort select="current-grouping-key()" />
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
</xsl:when>
<!--Select by node and group by child node-->
<xsl:when test="$from='node-childnode'">
<xsl:for-each-group select="$compose//*[name()=$sel]" group-by="child::*[name()=$group]">
<xsl:sort select="current-grouping-key()" />
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
</xsl:when>
What I want is to pass a parameter dictating whether it is an element-name or outputclass-attribute that is selected.
Then another parameter dictating what to group by: attribute or parent, child, following or preceding node.
What I have tried is below:
<xsl:variable name="oSel">
<xsl:choose>
<xsl:when test="starts-with($from,'oclass-')">
<xsl:value-of select="$compose//*[contains(#outputclass,$sel)]"/>
</xsl:when>
<xsl:when test="starts-with($from,'node-')">
<xsl:value-of select="$compose//*[name()=$sel]"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:variable name="oGroup">
<xsl:choose>
<xsl:when test="ends-with($from,'-attribute')">
<xsl:value-of select="*/#*[local-name()=$group]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-childnode')">
<xsl:value-of select="*/*[name()=$group]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-parentnode')">
<xsl:value-of select="parent::*[name()=$group]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-followingnode')">
<xsl:value-of select="following-sibling::*[name()=$group][1]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-precedingnode')">
<xsl:value-of select="preceding-sibling::*[name()=$group][1]"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:variable name="test">
<!--<xsl:for-each-group select="saxon:evaluate($oSel)" group-by="saxon:evaluate($oGroup)">-->
<xsl:for-each-group select="saxon:evaluate($oSel)" group-by="saxon:evaluate($oGroup)">
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
</xsl:variable>
I have looked into the saxon documentation and tried all sorts of solutions but none of them are working. Is it possible to do this?
Should have added that I am using Saxon 9.1.0.8 - Sorry, still new to XSLT
Firstly, saxon:evaluate() isn't the right tool for the job.
Now, why isn't it working? To declare the value of $oSel, you've done something like this:
<xsl:value-of select="$compose//*[contains(#outputclass,$sel)]"/>
which evaluates the expression in the select attribute and returns its result. But you're then passing $oSel to saxon:evaluate(), which expects a string containing an XPath expression. I think you're trying to bind the variable to the expression "$compose//*[contains(#outputclass,$sel)]", not to the result of evaluating this expression. To do that you would have to write
<xsl:value-of select="'$compose//*[contains(#outputclass,$sel)]'"/>
Note the extra quotes; but that would now fail because the expression passed to saxon:evaluate() can't explicitly use variables such as $compose (there's a mechanism to pass parameters, but you don't really want to go there).
In XSLT 3.0 saxon:evaluate is superseded by the standard instruction xsl:evaluate; but you don't want that one either.
The right mechanism to be using here is higher order functions.
In XSLT 3.0 you can write
<xsl:for-each-group select="$compose//*[$predicate(.)]" group-by="$grouping-key(.)">
Where $predicate and $grouping-key are variables bound to user-defined functions. You can bind these variables with logic like this:
<xsl:variable name="predicate" as="function(element()) as xs:boolean">
<xsl:choose>
<xsl:when test="starts-with($from,'oclass-')">
<xsl:sequence select="function($n){contains($n/#outputclass,$sel)}"/>
</xsl:when>
<xsl:when test="starts-with($from,'node-')">
<xsl:sequence select="function($n){name($n)=$sel}"/>
</xsl:when>
</xsl:choose>
</xsl:variable>
Change the xpath as below.
$compose//*[name()=$sel or contains(#outputclass,$sel)]
Here is the final code looks like
<xsl:when test="$from='oclass-attribute'">
<xsl:for-each-group select="$compose//*[name()=$sel or contains(#outputclass,$sel)]" group-by="#*[name()=$group]">
<xsl:sort select="current-grouping-key()" />
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
Try
select="if ($from='oclass-attribute') then $compose//*[contains(#outputclass,$sel)] else $compose//*[name()=$sel]"
and use the same approach for the group-by attribute:
group-by="if ($from='oclass-attribute') then #*[name()=$group] else child::*[name()=$group]"

How XSLT treats multiple nested element?

I want to convert some plain text with special marker into HTML formatted text.
For example,
This is the original value
Th<italic>is is a <under>com<bold>bina</bold>tion</under></italic> text.
The original value as actual value (just for reference)
Th<italic>is is a <under>com<bold>bina</bold>tion</under></italic> text.
HTML format I expect as a result
Th<i>is is a <u>com<b>bina</b>tion</u></i> text.
I tried with below template but it can not be parsed by XSLT parser.
<xsl:template name="decorateValue">
<xsl:param name="originalString" />
<xsl:variable name="preString" select="substring-before($originalString, '<')" />
<xsl:variable name="postString" select="substring-after($originalString, '<')" />
<xsl:value-of select="$preString" />
<xsl:variable name="tagName" select="substring-before($postString, '>')" />
<xsl:choose>
<xsl:when test="$tagName='bold'">
<xsl:element name="b">
</xsl:when>
<xsl:when test="$tagName='/bold'">
</xsl:element>
</xsl:when>
<xsl:when test="$tagName='italic'">
<xsl:element name="i">
</xsl:when>
<xsl:when test="$tagName='/italic'">
</xsl:element>
</xsl:when>
<xsl:when test="$tagName='under'">
<xsl:element name="u">
</xsl:when>
<xsl:when test="$tagName='/under'">
</xsl:element>
</xsl:when>
</xsl:choose>
<xsl:call-template name="decorateValue">
<xsl:with-param name="originalString"
select="substring-after($postString, '>')" />
</xsl:call-template>
</xsl:template>
any idea to solve this?
I would appreciate in advance.
If you can actually keep the original text, your life would be easier. Then you can transform
Th<italic>is is a <under>com<bold>bina</bold>tion</under></italic> text. easily into xHTML
<xsl:template match="italic">
<xsl:element name="i">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
You can't mix your tags in your example. You open an xsl:when and open an xsl:element and then close the xsl:when. That is not valid XML!
So if you want to go with the encoded string you need something like:
<xsl:template name="decorateValue">
<xsl:param name="originalString" />
<xsl:if test="$originalString!=''">
<xsl:variable name="preString" select="substring-before($originalString, '<')" />
<xsl:variable name="postString" select="substring-after($originalString, '<')" />
<xsl:variable name="endString" select="'magic happens here!'" />
<xsl:value-of select="$preString" />
<xsl:variable name="tagName" select="substring-before($postString, '>')" />
<xsl:variable name="restString" select="'magic happens here!'" />
<xsl:choose>
<xsl:when test="$tagName='bold'">
<xsl:element name="b">
<xsl:call-template name="decorateValue">
<xsl:with-param name="originalString"
select="$restString" />
</xsl:call-template>
</xsl:element>
</xsl:when>
<xsl:when test="$tagName='italic'">
<xsl:element name="i">
<xsl:call-template name="decorateValue">
<xsl:with-param name="originalString"
select="$restString" />
</xsl:call-template>
</xsl:element>
</xsl:when>
<xsl:when test="$tagName='under'">
<xsl:element name="u">
<xsl:call-template name="decorateValue">
<xsl:with-param name="originalString"
select="$restString" />
</xsl:call-template>
</xsl:element>
</xsl:when>
</xsl:choose>
<xsl:value-of select="$endString" />
</xsl:if>
</xsl:template>
You see 2 places where 'magic happens here'. This is where you need to apply the string-before-last and string-after-last patterns (which are a PITA in XSLT). The best explanation can be found in the XSLT Cookbook (and you want to have XSLT 2.0 at least.
Hope the pointers help. You might consider breaking the pattern into individual, so you don't fish for > alone, but the full tags. You still need to use the before-last / after-last functions.

embeding od condition in xsl when getting error

below is the xsl tag on which I am getting the error as i have used xsl:when intead of xsl:if , folks please advise how can I re correct it so that i do not get compilation exception while transforming the xsl again I am using xslt 1.0
<xsl:when test="abcid=dec_id">
<xsl:for-each select="$qq_Obj/ert_Period/ytr_Period">
<xsl:variable name="ABC_Rate">
<xsl:value-of select="$Sds/oht_Period/rew_Period/#vgRate" />
</xsl:variable>
<xsl:choose>
<xsl:when test="$iue_first=#vgRate">
<xsl:value-of select="'AAA'" />
</xsl:when>
<xsl:value-of select="'BBB'" />
</xsl:choose>
</xsl:for-each>
</xsl:when>
You need to enclose the "else" part of an xsl:choose in an xsl:otherwise:
<xsl:choose>
<xsl:when test="$iue_first=#vgRate">
<xsl:value-of select="'AAA'" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'BBB'" />
</xsl:otherwise>
</xsl:choose>

Nesting of xsl:when conditions

I have a query related to xsl choose , inside xsl choose is being used
Now i have two xsl:when condition is there like as hsown below
<xsl:choose>
<xsl:when test=$labcVar=$cde>
<xsl:when test="$erf=$der">
<xsl:value-of select="'edr'" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'fre'" />
</xsl:otherwise>
please advise is it the correct approach since insise xsl:when ..I have to begain only when first when condition becomes true the only I should go for second xsl:when condition, please advsie it is aloowed or not
You can't nest a when directly inside a when, but you can use another choose:
<xsl:choose>
<xsl:when test=$labcVar=$cde>
<xsl:choose>
<xsl:when test="$erf=$der">
<xsl:value-of select="'edr'" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'fre'" />
</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
How about using and
<xsl:when test="$labcVar=$cde and $erf=$der">
<xsl:value-of select="'edr'" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'fre'" />
</xsl:otherwise>