XSL: replace single and double quotes with ' and " - xslt

I have a XSL which I am using to transform one XML to another like:
<xsl:value-of select="somestatement"/>
where some statement has single quotes and double quotes. Ex: This is an "example" string. I have 'single quotes'
I want to replace single quotes with &apos; and double quotes with " so that output string will be :
This is an "example" string. I have &apos;single quotes&apos;
Can someone please suggest a solution for this?
Thanks for the help in advance.

You need to call a named recursive template for this. Try:
<xsl:template name="escape-quotes">
<xsl:param name="text"/>
<xsl:param name="searchString">'</xsl:param>
<xsl:param name="replaceString">&apos;</xsl:param>
<xsl:variable name="apos">'</xsl:variable>
<xsl:choose>
<xsl:when test="contains($text,$searchString)">
<xsl:call-template name="escape-quotes">
<xsl:with-param name="text" select="concat(substring-before($text,$searchString), $replaceString, substring-after($text,$searchString))"/>
<xsl:with-param name="searchString" select="$searchString"/>
<xsl:with-param name="replaceString" select="$replaceString"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$searchString=$apos">
<xsl:call-template name="escape-quotes">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="searchString">"</xsl:with-param>
<xsl:with-param name="replaceString">&quot;</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" disable-output-escaping="yes"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Example of calling the template:
<output>
<xsl:call-template name="escape-quotes">
<xsl:with-param name="text">This is an "example" string. I have 'single quotes'.</xsl:with-param>
</xsl:call-template>
</output>
Result:
<output>This is an "example" string. I have &apos;single quotes&apos;.</output>

Related

XSLT , valid JSON [duplicate]

How do you replace an xml value, for example:
<name>Linda O'Connel</name>
to:
<name>Linda O''Connel</name>
via XSLT?
I need this because I have to pass this value in a powershell command line and to other platforms since the "double single quote" is needed to escape the apostrophe/single quotes.
Assuming an XSLT 1.0 processor, you will need to use a recursive named template for this, e.g:
<xsl:template name="replace">
<xsl:param name="text"/>
<xsl:param name="searchString">'</xsl:param>
<xsl:param name="replaceString">''</xsl:param>
<xsl:choose>
<xsl:when test="contains($text,$searchString)">
<xsl:value-of select="substring-before($text,$searchString)"/>
<xsl:value-of select="$replaceString"/>
<!-- recursive call -->
<xsl:call-template name="replace">
<xsl:with-param name="text" select="substring-after($text,$searchString)"/>
<xsl:with-param name="searchString" select="$searchString"/>
<xsl:with-param name="replaceString" select="$replaceString"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Example of call:
<xsl:template match="name">
<xsl:copy>
<xsl:call-template name="replace">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
You can also try out the following.
<xsl:variable name="temp">'</xsl:variable>
<name>
<xsl:value-of select="concat(substring-before(name,$temp),$temp,$temp,substring-after(name,$temp))"/>
</name>

How to use regex in xsl

I have this request coming in xml. Its a CDATA
<cmd>
<![CDATA[HG<><><36.75><0420>< ><HS6011201700446279><><>< >< >< ><><>< ><>< >< ><>< ><>< ><>]]>
</cmd>
I need to extract HS6011201700446279 from the cdata path.
Following is the regex they gave. How to use this in xsl
HG<\\s*><\\s*><.*><.*><.*><[A-Z]{2}(\\d{10,}).*
There is no regex support in XSLT 1.0. Assuming that the sub-string you want is within the 6th "tag" of the given string, you could extract it by calling a recursive named template:
<xsl:template match="cmd">
<result>
<xsl:call-template name="get-Nth-value">
<xsl:with-param name="list" select="."/>
<xsl:with-param name="N" select="6"/>
</xsl:call-template>
</result>
</xsl:template>
<xsl:template name="get-Nth-value">
<xsl:param name="list"/>
<xsl:param name="N"/>
<xsl:param name="delimiter" select="'><'"/>
<xsl:choose>
<xsl:when test="$N = 1">
<xsl:value-of select="substring-before(concat($list, $delimiter), $delimiter)"/>
</xsl:when>
<xsl:when test="contains($list, $delimiter) and $N > 1">
<!-- recursive call -->
<xsl:call-template name="get-Nth-value">
<xsl:with-param name="list" select="substring-after($list, $delimiter)"/>
<xsl:with-param name="N" select="$N - 1"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:template>

Replace & with %26 in XSLT 1.0

I have XML coming back from a search engine with nodes like so
<team>Some team name with &</team>
I need to make a link of the team and while it might not be the optimal way it worked till I discovered that some team names include an ampersand
What I have is (team surrounded by double quotes)
<xsl:variable name="teamend" select="concat(team,'%22')"/>
<a href="{concat('http://site/page.aspx?k=team%3D%22', $teamend)}">
<xsl:call-template name="DisplayCustomField">
<xsl:with-param name="customfield" select="team" />
</xsl:call-template>
but if team contains an ampersand the link will be broken, how can I best fix this?
Thanks in advance
You can use this named template and call it before using the string in team:
<xsl:template name="replace">
<xsl:param name="string"/>
<xsl:param name="substring"/>
<xsl:param name="replacement"/>
<xsl:choose>
<xsl:when test="contains($string, $substring)">
<xsl:value-of select="substring-before($string, $substring)"/>
<xsl:value-of select="$replacement"/>
<xsl:call-template name="replace">
<xsl:with-param name="substring" select="$substring"/>
<xsl:with-param name="replacement" select="$replacement"/>
<xsl:with-param name="string" select="substring-after($string, $substring)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
You can call it like this, and replace any substring with another:
<xsl:variable name="string-with-escaped-ampersand">
<xsl:call-template name="replace">
<xsl:with-param name="string" select="team"/>
<xsl:with-param name="substring">&</xsl:with-param>
<xsl:with-param name="replacement">%26</xsl:with-param>
</xsl:call-template>
</xsl:variable>
And use it in your code:
<xsl:variable name="teamend" select="concat($string-with-escaped-ampersand,'%22')"/>
As #Tomalak pointed out in the comments, since you are generating an URL you might have to deal with several other characters which need to be encoded. There are functions for that in XSLT 2.0, but in XSLT 1.0 you will be limited to templates or extensions.

Calculating string length of text node when parent has other child nodes too

I am stuck with XSLT 1.0 making a hyphenation script for verbatim output to PDF. (note it is PDF so I can't use clever CSS to hellp me out.
I would like to be able to access both text() nodes and child text() nodes to compare stringlength as part of the script.
For example:
<programlisting>This text will be displayed without word-wrap
I can measure length of string between end line chars '
'
and break long lines. That works great until
<emphasis>this gets added</emphasis> then my counting loop doesn't
count the text in the emphasis tags.
</programlisting>
I am looping through the text() using a template called screen. It detects the end of line character and decides if the line is too long. Long lines are broken and the remainder recursed through the template until the remainder is less than the max line length... then onto the next line of text...
... that works great, but now i have some children in the and I can't figure out how I can access the text() and any child node's text() at the same time to calculate string length.
Sorry for the long example code...
The template to match looks like this...
<xsl:template match="programlisting/text()">
<xsl:variable name="max_width">100</xsl:variable>
<xsl:variable name="min_width">80</xsl:variable>
<xsl:call-template name="screen">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="max_width" select="$max_width"/>
<xsl:with-param name="min_width" select="$min_width"/>
</xsl:call-template>
</xsl:template>
The template to decide if string before linebreak is too long:
<xsl:template name="screen">
<xsl:param name="text" select="."/>
<xsl:param name="max_width"/>
<xsl:param name="min_width"/>
<xsl:variable name="fixed_text"> <!-- add end linebreak if missing -->
<xsl:choose>
<xsl:when test="substring($text,string-length($text),1) = '
'">
<xsl:value-of select="$text"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($text,'
')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="first_line">
<xsl:value-of select="substring-before(concat($fixed_text,'
'),'
')"/>
</xsl:variable>
<xsl:variable name="other_lines">
<xsl:value-of select="substring-after($fixed_text,concat($first_line,'
'))"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="string-length(normalize-space($first_line)) < 1"><!-- blank line (just trim and copy)-->
<xsl:value-of select="concat($verbatim_padding,substring($first_line,1,100),'
')"/>
</xsl:when>
<xsl:when test="string-length($first_line) < 100"> <!-- short line (just copy)-->
<xsl:value-of select="concat($verbatim_padding,$first_line,'
')"/>
</xsl:when>
<xsl:otherwise>
<!-- Line is too long!! -->
<xsl:variable name="wrapped_lines">
<xsl:call-template name="break_line">
<xsl:with-param name="long_string" select="$first_line"/>
<xsl:with-param name="max_chars" select="100"/>
<xsl:with-param name="min_chars" select="80"/>
<xsl:with-param name="break_chars" select="' ,/;'"/>
<xsl:with-param name="linebreak_string" select="'~'"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat($verbatim_padding,$wrapped_lines)"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="string-length($other_lines) > 0">
<xsl:call-template name="screen">
<xsl:with-param name="text" select="$other_lines"/>
<xsl:with-param name="max_width" select="$max_width"/>
<xsl:with-param name="min_width" select="$min_width"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
The template to break up long lines:
<xsl:template name="break_line">
<xsl:param name="long_string"/>
<xsl:param name="max_chars"/> <!--max chars allowed on a line -->
<xsl:param name="min_chars"/> <!--max char position foa soft linebreak (else we hard break at the max chars!) -->
<xsl:param name="break_chars"/> <!-- chars used for soft breaking -->
<xsl:param name="linebreak_string"/> <!-- add this to end of a broken line -->
<xsl:choose>
<xsl:when test="(string-length($long_string) < $max_chars) or (string-length($long_string) < $min_chars) or (string-length($long_string) < 1)">
<xsl:value-of select="concat($long_string,'
')"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="trim_x_by">
<xsl:call-template name="CheckLastChar">
<xsl:with-param name="string" select="$long_string"/>
<xsl:with-param name="start" select="$max_chars"/>
<xsl:with-param name="stop" select="$min_chars"/>
<xsl:with-param name="break_chars" select="$break_chars"/>
<xsl:with-param name="max_chars" select="$max_chars"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="first_x_chars"><xsl:value-of select="substring($long_string,1,$trim_x_by)"/></xsl:variable>
<xsl:variable name="remaining_chars"><xsl:value-of select="substring-after($long_string,$first_x_chars)"/></xsl:variable>
<xsl:value-of select="concat($first_x_chars,' ',$linebreak_string)"/>
<xsl:call-template name="break_line">
<xsl:with-param name="long_string" select="$remaining_chars"/>
<xsl:with-param name="max_chars" select="$max_chars"/>
<xsl:with-param name="min_chars" select="$min_chars"/>
<xsl:with-param name="break_chars" select="$break_chars"/>
<xsl:with-param name="linebreak_string" select="$linebreak_string"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
And finally the recursive checking of string chars to see if its a safe place to line-break (soft break)
<xsl:template name="CheckLastChar">
<xsl:param name="string"/>
<xsl:param name="stop" select="1"/>
<xsl:param name="start" select="string-length($string)"/>
<xsl:param name="count" select="$start"/>
<xsl:param name="break_chars"/>
<xsl:param name="max_chars"/>
<xsl:choose>
<xsl:when test="($count < $stop) or (string-length($string) < $stop)">
<!-- gone so far into the line that a linebreak would look weird! So return the max-length and that will
force a 'hard' linebreak exactly at the max-length point regardless of the character -->
<xsl:value-of select="$max_chars"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="last_char"><xsl:value-of select="substring($string,$count,1)"/></xsl:variable>
<xsl:choose>
<xsl:when test="contains($break_chars,$last_char)">
<xsl:value-of select="$count"/>
</xsl:when>
<xsl:otherwise>
<!-- keep looking -->
<xsl:call-template name="CheckLastChar">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="stop" select="$stop"/>
<xsl:with-param name="start" select="$start"/>
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="break_chars" select="$break_chars"/>
<xsl:with-param name="max_chars" select="$max_chars"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
To get all of the text in an element (including its descendants' text), just use string(Element):
<xsl:template match="programlisting">
<xsl:variable name="max_width">100</xsl:variable>
<xsl:variable name="min_width">80</xsl:variable>
<xsl:call-template name="screen">
<xsl:with-param name="text" select="string(.)"/>
<xsl:with-param name="max_width" select="$max_width"/>
<xsl:with-param name="min_width" select="$min_width"/>
</xsl:call-template>
</xsl:template>
Note the removal of text() from the match attribute.
XPath Spec
The string-value of an element node is the concatenation of the string-values of all text node descendants of the element node in document order.
The string() function converts an object to a string as follows:
A node-set is converted to a string by returning the string-value of the node in the node-set that is first in document order. If the node-set is empty, an empty string is returned.

xslt 1.0 string replace function

I have a string "aa::bb::aa"
and need to turn it in to "aa, bb, aa"
I have tried
translate(string,':',', ')
but this returns "aa,,bb,,aa"
How can this be done.
A very simple solution (that will work as long as your string value doesn't have spaces):
translate(normalize-space(translate('aa::bb::cc',':',' ')),' ',',')
translate ":' into " "
normalize-space() to collapse multiple whitespace characters into a single space " "
translate single spaces " " into ","
A more robust solution would be to use a recursive template:
<xsl:template name="replace-string">
<xsl:param name="text"/>
<xsl:param name="replace"/>
<xsl:param name="with"/>
<xsl:choose>
<xsl:when test="contains($text,$replace)">
<xsl:value-of select="substring-before($text,$replace)"/>
<xsl:value-of select="$with"/>
<xsl:call-template name="replace-string">
<xsl:with-param name="text"
select="substring-after($text,$replace)"/>
<xsl:with-param name="replace" select="$replace"/>
<xsl:with-param name="with" select="$with"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
You can use it like this:
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="'aa::bb::cc'"/>
<xsl:with-param name="replace" select="'::'" />
<xsl:with-param name="with" select="','"/>
</xsl:call-template>
You can use this
Syntax:- fn:tokenize(string,pattern)
Example: tokenize("XPath is fun", "\s+")
Result: ("XPath", "is", "fun")