XSL Using replace and matches to update values - xslt

I am trying to update an old form that uses a data model that has changed, so any reference to the old model I want to replace with the new. I currently use the function matches to tell if I should preform the replace on the current string and then use the replace function to replace the value with the new one, the problem is that the regex used in the matches does not work with the regex in the replace.
<xsl:template
match="//*[contains(#*:default,'instance(''document'')/')
mode="pass">
<xsl:variable
name="regex"
as="element()*">
<regex>instance('document')/doc_type/description</regex>
<regex>anotherRegex</regex>
</xsl:variable>
<xsl:variable
name="replacement"
as="element()*">
<replacement>xxf:get-request-parameter('documentDesc')</replacement>
<replacement>replacedRegex</replacement>
</xsl:variable>
<xsl:element name="{name()}">
<xsl:for-each select="#*">
<xsl:choose>
<xsl:when test="name() = ('xxf:default')">
<xsl:attribute name="xxf:default">
<xsl:analyze-string
regex="{concat('(',$regex[1],'|',$regex[2],')')}"
select=".">
<xsl:matching-substring>
<xsl:if test="matches(.,$regex[1])">
<xsl:value-of select="replace(.,$regex[1],$replacement[1])" />
</xsl:if>
<xsl:if test="matches(.,$regex[2])">
<xsl:value-of select="replace(.,$regex[2],$replacement[2])" />
</xsl:if>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="." />
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="{local-name()}"><xsl:value-of select="." /></xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:element>
</xsl:template>
Current XML:
<xf:bind id="clinic-bind"
name="clinic"
xxf:default="instance('document')/doc_type/description"
type="xf:string"/>
What I want to turn it into:
<xf:bind id="clinic-bind"
name="clinic"
xxf:default="xxf:get-request-parameter('documentDesc')"
type="xf:string"/>

So the Problem was basicaally I had to escape the '()' characters in the regex variable.
So I ended up with
<regex>instance\('document'\)/doc_type/description</regex>
In the regex part.

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 to remove hyphen from string +xslt

how to remove hyphen from string like "19650512-0065" to "196505120065"
using this template : passing theID =
<xsl:template name="unformatLFPartyID">
<xsl:param name="theID" select="." />
<xsl:variable name="idSuffix" select="string-length($theID) - 3" />
<xsl:choose>
<xsl:when test="contains($theID,'-')">
<xsl:value-of select="substring($theID,0,$idSuffix)" />
<xsl:value-of select="substring($theID, $idSuffix)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$theID" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Try replacing the xsl:variable and entire xsl:choose with:
<xsl:value-of select="translate($theID,'-','')"/>

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.

XSLT 1.0 select value-of in node set with string

I have some trouble with selecting values in node set. I have a string variable,
which concatenate in path of next existed node in xml. But when i try to select valu from it,
it results in paste of value of this variable, not the value of node. I can`t find how i can
convert string to node set for proper selection. Please, help.
<xsl:for-each select="result/node()">
<xsl:copy>
<xsl:for-each select="./node()">
<xsl:copy>
<xsl:attribute name="rating">
<xsl:text>0</xsl:text>
</xsl:attribute>
<xsl:choose>
<xsl:when test="translate(
substring(.,1,3),
$upCase,
$lowCase
) = 'id_'">
<xsl:value-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="cval"
select="concat(
'/survey/checkbox_value/',
local-name(),
'/.'
)" />
<xsl:value-of select="$cval" />
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:for-each>
You don't need to convert anything. It could be done with plain xpath.
<xsl:variable name="local_name" select="local-name()"/>
<xsl:value-of select="/survey/checkbox_value/node()[
local-name() = $local_name
]"/>