Hi am trying to replace few characters in a xml with some values using xsl in datapower. i got few snippets from stackoverflow , but i guess its not working.
<xsl:stylesheet extension-element-prefixes="dp" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="dp" version="1.0" xmlns:dp="http://www.datapower.com/extensions" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="Output">
<xsl:copy-of select="dp:variable('var://context/INPUT')"/>
</xsl:variable>
<xsl:message dp:priority="debug">*** before replace ***<xsl:copy-of select="$Output"/> ***</xsl:message>
<xsl:if test="contains($Output,'&')">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="$Output"/>
<xsl:with-param name="replace" select="'&'"/>
<xsl:with-param name="by" select="'%26amp%3B'"/>
</xsl:call-template>
</xsl:if>
<xsl:message dp:priority="debug">*** after and***<xsl:copy-of select="$Output"/> ***</xsl:message>
<xsl:if test="contains($Output,'>')">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="$Output"/>
<xsl:with-param name="replace" select="'>'"/>
<xsl:with-param name="by" select="'%26gt%3B'"/>
</xsl:call-template>
</xsl:if>
<xsl:message dp:priority="debug">*** IsoClaimsearch after gt***<xsl:copy-of select="$Output"/> ***</xsl:message>
<xsl:if test="contains($Output,'<')">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="$Output"/>
<xsl:with-param name="replace" select="'<'"/>
<xsl:with-param name="by" select="'%26lt%3B'"/>
</xsl:call-template>
</xsl:if>
<xsl:message dp:priority="debug">*** after lt***<xsl:copy-of select="$Output"/> ***</xsl:message>
<xsl:if test="contains($Output,'"')">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="$Output"/>
<xsl:with-param name="replace" select="'"'"/>
<xsl:with-param name="by" select="'%26quot%3B'"/>
</xsl:call-template>
</xsl:if>
<xsl:message dp:priority="debug">*** after quot***<xsl:copy-of select="$Output"/> ***</xsl:message>
<xsl:copy-of select="$Output"/>
<xsl:message dp:priority="debug">*** after replace ***<xsl:copy-of select="$Output"/> ***</xsl:message>
</xsl:template>
<xsl:template name="string-replace-all">
<xsl:param name="text"/>
<xsl:param name="replace"/>
<xsl:param name="by"/>
<xsl:choose>
<xsl:when test="contains($text, $replace)">
<xsl:value-of select="substring-before($text,$replace)"/>
<xsl:value-of select="$by"/>
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="substring-after($text,$replace)"/>
<xsl:with-param name="replace" select="$replace"/>
<xsl:with-param name="by" select="$by"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
am not sure what i have missed in this . can somebody please help me
I have the following function which replaces all occurences of a search-string ($replace) in a string ($text) with another string ($by):
<xsl:template name="string-replace-all">
<xsl:param name="text" />
<xsl:param name="replace" />
<xsl:param name="by" />
<xsl:choose>
<xsl:when test="contains($text, $replace)">
<xsl:value-of select="substring-before($text,$replace)" />
<xsl:value-of select="$by" />
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text"
select="substring-after($text,$replace)" />
<xsl:with-param name="replace" select="$replace" />
<xsl:with-param name="by" select="$by" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This works fine for replacing text in individual strings, however it doesn't work when trying to replace text in a node-set.
What I am looking for is a function which takes, for example, the following XML document:
<nodeSet>
<node>a1;a2;a3</node>
<node>b1;b2;b3</node>
</nodeSet>
and outputs the following:
<nodeSet>
<node>a1#a2#a3</node>
<node>b1#b2#b3</node>
</nodeSet>
The following template does the job when the target and replacement strings are known in advance:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl"
>
<xsl:template name="string-replace-all-in-nodeset">
<xsl:param name="nodeset" />
<xsl:apply-templates select="exsl:node-set($nodeset)" mode="str-repl-in-nodeset"/>
</xsl:template>
<xsl:template match="*/text()" mode="str-repl-in-nodeset">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="replace" select=" ';' "/>
<xsl:with-param name="by" select=" '#' "/>
</xsl:call-template>
</xsl:template>
<xsl:template name="string-replace-all">
<xsl:param name="text" />
<xsl:param name="replace" />
<xsl:param name="by" />
<xsl:choose>
<xsl:when test="contains($text, $replace)">
<xsl:value-of select="substring-before($text,$replace)" />
<xsl:value-of select="$by" />
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="substring-after($text,$replace)" />
<xsl:with-param name="replace" select="$replace" />
<xsl:with-param name="by" select="$by" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#*|node()" mode="str-repl-in-nodeset">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="str-repl-in-nodeset"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
However, I need to be able to pass the target and replacement string (';' and '#' in this case) dynamically. Is there any way of passing these parameters to the template matching all text nodes (match="*/text()") or any other way of achieving what I want?
Here is a stylesheet that defines global parameters for the replace and by strings and then passes them on to all templates in that mode str-repl-in-nodeset:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl">
<xsl:param name="ns1">
<nodeSet>
<node>a1;a2;a3</node>
<node>b1;b2;b3</node>
</nodeSet>
</xsl:param>
<xsl:param name="replace" select="';'"/>
<xsl:param name="by" select="'#'"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="exsl:node-set($ns1)" mode="str-repl-in-nodeset">
<xsl:with-param name="replace" select="$replace"/>
<xsl:with-param name="by" select="$by"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="/ | #* | node()" mode="str-repl-in-nodeset">
<xsl:param name="replace"/>
<xsl:param name="by"/>
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="str-repl-in-nodeset">
<xsl:with-param name="replace" select="$replace"/>
<xsl:with-param name="by" select="$by"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="*/text()" mode="str-repl-in-nodeset">
<xsl:param name="replace"/>
<xsl:param name="by"/>
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="replace" select="$replace"/>
<xsl:with-param name="by" select="$by"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="string-replace-all">
<xsl:param name="text" />
<xsl:param name="replace" />
<xsl:param name="by" />
<xsl:choose>
<xsl:when test="contains($text, $replace)">
<xsl:value-of select="substring-before($text,$replace)" />
<xsl:value-of select="$by" />
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="substring-after($text,$replace)" />
<xsl:with-param name="replace" select="$replace" />
<xsl:with-param name="by" select="$by" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:transform>
I've got a name string in my XML whose length varies. In case of a long name, it expands horizontally which I cannot afford. How can I make it grow vertically? Below is what I have.
<fo:table-cell font-family="Courier" font-size="10px" display-align="before"height="0.01042in" border-style="solid" border-color="green">
<fo:block padding-top="1pt" padding-bottom="1pt" borderstyle="solid">
<fo:inline>
<xsl:text>USER INFORMATION</xsl:text>
</fo:inline>
<fo:block>
<xsl:text>
</xsl:text>
</fo:block>
<fo:inline font-family="Courier" font-size="12px" font-weight="bold">
<xsl:value-of select="USERNAME" />
</fo:inline>
</fo:block>
</fo:table-cell>
I used that link posted by Joel above in comments. When I applied template exactly, it displayed strings with odd length fine but missed the last character of even length strings. I then changed the comparison from less than< to less than or equal<= and it worked.
Hope it will help someone.Thanks.
<xsl:template name="zero_width_space_1">
<xsl:param name="data" />
<xsl:param name="counter" select="0" />
<xsl:choose>
<xsl:when test="$counter <= string-length($data)">
<xsl:value-of select='concat(substring($data,$counter,1),"")' />
<xsl:call-template name="zero_width_space_2">
<xsl:with-param name="data" select="$data" />
<xsl:with-param name="counter" select="$counter+1" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="zero_width_space_2">
<xsl:param name="data" />
<xsl:param name="counter" />
<xsl:value-of select='concat(substring($data,$counter,1),"")' />
<xsl:call-template name="zero_width_space_1">
<xsl:with-param name="data" select="$data" />
<xsl:with-param name="counter" select="$counter+1" />
</xsl:call-template>
</xsl:template>
<xsl:call-template name="zero_width_space_1">
<xsl:with-param name="data" select="mystring" />
</xsl:call-template>
I have a long String separated with the equal sign(=)as delimiter,something like this:
AAA=BBBBB=CCCCCCCCCCCCCCCC=D=FFFFF=GG=H
The substrings can have arbitrary length.If I want to get the first substring I can use the substring-before function like this:
<xsl:value-of select="substring-before($vari, '=')"/>
But is there a way to get only the second, (third, etc) substring?
I need BBBBB instead of AAA=BBBBB and CCCCCCCCCCCCCCCC instead of AAA=BBBBB=CCCCCCCCCCCCCCCC so substring-before-last won't work.
FYI, if you have EXSLT's String extension elements available, you can do something like this:
<xsl:value-of select="str:tokenize($vari, '=')[2]" />
Using your above string, this would return BBBBB.
In XPath 2.0, this function is built in:
<xsl:value-of select="tokenize($vari, '=')[2]" />
With xslt-1.0:
This could be done by a combination of substring-after and substring-before.
Try:
<xsl:value-of select="substring-before(
substring-after ($vari, , '=')
, '=')"/>
For BBBBB
Or:
<xsl:value-of select="substring-before(
substring-after ( substring-after ($vari, '='), '=')
, '=')"/>
For CCCCCCCCCCCCCCCC.
Attention: Not tested.
With xslt-2.0 there may be better ways.
Here is an xslt-1.0 solution that doesn't require the use of extensions and works with arbitrary separator lengths an separator occurrences:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:call-template name="substring-nth-occurrence-position">
<xsl:with-param name="string">aaa_#bbb_#ccc_#dddd_#ee</xsl:with-param>
<xsl:with-param name="substr">_#</xsl:with-param>
<xsl:with-param name="occurrence">3</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="substring-nth-occurrence-position">
<xsl:param name="string"/>
<xsl:param name="substr"/>
<xsl:param name="occurrence"/>
<xsl:param name="current_iteration" select="1"/>
<xsl:param name="accumulated_length" select="0"/>
<xsl:choose>
<!-- in case occurrence is greater than the number of instances of substr return 0 -->
<xsl:when test="not(contains($string, $substr))">0</xsl:when>
<xsl:otherwise>
<!-- determine the position of the next instance of substr in the (remaining part of ) string and add it to accumulated_length-->
<xsl:variable name="v.accumulated_length_new">
<xsl:variable name="v.idx.of.substr">
<xsl:call-template name="instr">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="substr" select="$substr"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$accumulated_length + $v.idx.of.substr"></xsl:value-of>
</xsl:variable>
<!-- if current_iteration equals occurrence then return accumulated length... -->
<xsl:choose>
<xsl:when test="$current_iteration= $occurrence ">
<xsl:value-of select="$v.accumulated_length_new" />
</xsl:when>
<!-- ... else run another iteration-->
<xsl:otherwise>
<xsl:call-template name="substring-nth-occurrence-position">
<xsl:with-param name="string" select="substring-after($string, $substr)"/>
<xsl:with-param name="substr" select="$substr"/>
<xsl:with-param name="occurrence" select="$occurrence"/>
<xsl:with-param name="current_iteration" select="$current_iteration + 1"/>
<xsl:with-param name="accumulated_length" select="$v.accumulated_length_new + string-length($substr) - 1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- returns the position of the first occurrence of a substring in a string -->
<xsl:template name="instr">
<xsl:param name="string"/>
<xsl:param name="substr"/>
<xsl:value-of select="contains($string,$substr)*(1 + string-length(substring-before($string,$substr)))" />
</xsl:template>
</xsl:stylesheet>
(Execution result of the above example is 14).
I'm trying to format strings in XSLT that needs to be in pascal case to be used appropriately for the application I'm working with.
For example:
this_text would become ThisText
this_long_text would become ThisLongText
Is it possible to also set this up where I can send an input to the format so I do not have to recreate the format multiple times?
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vLower" select=
"'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="vUpper" select=
"'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:call-template name="Pascalize">
<xsl:with-param name="pText" select="concat(., '_')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="Pascalize">
<xsl:param name="pText"/>
<xsl:if test="$pText">
<xsl:value-of select=
"translate(substring($pText,1,1), $vLower, $vUpper)"/>
<xsl:value-of select="substring-before(substring($pText,2), '_')"/>
<xsl:call-template name="Pascalize">
<xsl:with-param name="pText"
select="substring-after(substring($pText,2), '_')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<t>
<a>this_text</a>
<b>this_long_text</b>
</t>
produces the desired result:
<t>
<a>ThisText</a>
<b>ThisLongText</b>
</t>
BTW, this is camelCase and this is PascalCase
Here, two years after the fact, is an XSLT 2.0 solution:
<xsl:function name="fn:pascal-case">
<xsl:param name="string"/>
<xsl:value-of select="string-join(for $s in tokenize($string,'\W+') return concat(upper-case(substring($s,1,1)),substring($s,2)),'')"/>
</xsl:function>
It will pascalize either 'this_long_text' or 'this-long-text' to 'ThisLongText' because it breaks on any non-word characters.
In the regex flavors I am most familiar with (perl, pcre, etc.), an underscore is considered part of the '\w' character class (therefore not part of \W), but for XSLT 2.0 the XSD datatypes are used (http://www.w3.org/TR/xmlschema-2/) and '\w' is defined as:
[#x0000-#x10FFFF]-[\p{P}\p{Z}\p{C}] (all characters except the set of "punctuation", "separator" and "other" characters)
so '\W' includes an underscore.
This version worked for me. I added a choose that outputs "the rest" of the string when no more underbars are present.
<xsl:variable name="vLower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="vUpper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:template name="Pascalize">
<xsl:param name="pText" />
<xsl:if test="$pText">
<xsl:value-of select="translate(substring($pText,1,1), $vLower, $vUpper)" />
<xsl:choose>
<xsl:when test="contains($pText, '_')">
<xsl:value-of select="substring-before(substring($pText,2), '_')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($pText,2)" />
</xsl:otherwise>
</xsl:choose>
<xsl:call-template name="Pascalize">
<xsl:with-param name="pText" select="substring-after(substring($pText,2), '_')" />
</xsl:call-template>
</xsl:if>
</xsl:template>
Also, in case anyone comes here looking for the reverse process (which I happened to also require today and could find not a single example of anywhere)...
<xsl:variable name="vLower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="vUpper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:template name="TitleCase">
<xsl:param name="pText" />
<xsl:call-template name="TitleCase_recurse">
<xsl:with-param name="pText" select="concat(translate(substring($pText,1,1), $vLower, $vUpper), substring($pText,2))" />
</xsl:call-template>
</xsl:template>
<xsl:template name="TitleCase_recurse">
<xsl:param name="pText" />
<xsl:if test="string-length($pText) > 1">
<xsl:if test="not(substring($pText,1,1) = ' ' and substring($pText,1,1) = ' ')">
<xsl:value-of select="substring($pText,1,1)" />
</xsl:if>
<xsl:if test="translate(substring($pText,1,1), $vLower, $vUpper) != substring($pText,1,1)">
<xsl:if test="translate(substring($pText,2,1), $vLower, $vUpper) = substring($pText,2,1)">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:if>
<xsl:call-template name="TitleCase_recurse">
<xsl:with-param name="pText" select="substring($pText,2)" />
</xsl:call-template>
</xsl:if>
<xsl:if test="string-length($pText) = 1">
<xsl:value-of select="$pText" />
</xsl:if>
</xsl:template>
I love it when my subconscious brain pops up an answer a few hours after I've completely given up consciously. ;-)
I was trying to achieve the "pascalizing" with the following XLST function call:
<xsl:value-of select="fn:replace(#name,'_(\w{1})','\U$1')"/>
Unfortunately the processor throws the error message "Invalid replacement string in replace():
\ character must be followed by \ or $"
the problem is the \U modifier which is supposed to do the uppercase conversion of the matched pattern. If I change it to
<xsl:value-of select="fn:replace(#name,'_(\w{1})','\\U$1')"/>
the output string contains the sequence '\U' because it is now esacped - but I don't want to escape it, I want it do be effective ;-) . I did test
<xsl:value-of select="fn:replace(#name,'_(\w{1})','$1')"/>
(without converting the match to uppercase) and that works fine. But of course it does no uppercasing, just removes underscores and replaces the letter after the underscore by itself instead of capitalizing it. Am I doing something wrong here or is the \U modifier simply not supported in the regex implementation of my XSLT processor?
Thanks to Dimitre, I was able to get most of the way there. When running my strings through the Pascalize template, the bit after the last '_' was cut off. There's probably a cleaner way of doing it, but here's the code I used:
<xsl:template name="Pascalize">
<xsl:param name="pText"/>
<xsl:if test="$pText">
<xsl:value-of select="translate(substring($pText,1,1), $vLower, $vUpper)"/>
<xsl:value-of select="substring-before(substring($pText,2), '_')"/>
<xsl:call-template name="Pascalize">
<xsl:with-param name="pText" select="substring-after(substring($pText,2), '_')"/>
</xsl:call-template>
<xsl:call-template name="GrabLastPart">
<xsl:with-param name="pText" select="$pText"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="GrabLastPart">
<xsl:param name="pText"/>
<xsl:choose>
<xsl:when test="contains($pText, '_')">
<xsl:call-template name="GrabLastPart">
<xsl:with-param name="pText" expr="substring-after($pText, '_')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($pText, 2)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>