XSLT Version 1 replace single/double quotes - xslt

I am trying to convert replace single/double quotes with " and ' respectively in xml
I am very new to xsl so very much appreciate if someone can help

For a dynamic method of replacing it will be better to create separate template with parameters as input text, what to replace and replace with.
So, in example input text is:
Your text "contains" some "strange" characters and parts.
In below XSL example, you can see replacing of " (") with " and ' ("'):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<!--template to replace-->
<xsl:template name="template-replace">
<xsl:param name="param.str"/>
<xsl:param name="param.to.replace"/>
<xsl:param name="param.replace.with"/>
<xsl:choose>
<xsl:when test="contains($param.str,$param.to.replace)">
<xsl:value-of select="substring-before($param.str, $param.to.replace)"/>
<xsl:value-of select="$param.replace.with"/>
<xsl:call-template name="template-replace">
<xsl:with-param name="param.str" select="substring-after($param.str, $param.to.replace)"/>
<xsl:with-param name="param.to.replace" select="$param.to.replace"/>
<xsl:with-param name="param.replace.with" select="$param.replace.with"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$param.str"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/">
<xsl:call-template name="template-replace">
<!--put your text with quotes-->
<xsl:with-param name="param.str">Your text "contains" some "strange" characters and parts.</xsl:with-param>
<!--put quote to replace-->
<xsl:with-param name="param.to.replace">"</xsl:with-param>
<!--put quot and apos to replace with-->
<xsl:with-param name="param.replace.with">"'</xsl:with-param>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
Then result of replacing will be as below:
Your text "'contains"' some "'strange"' characters and parts.
Hope it will help.

Related

Order values in variable in xslt or whit

How can I order values in a variable that contains strings that are comma-separated?
It would be OK if the variable was separated on sub-strings of 001a and so on.
My variable is a string of values separated by commas, but because I join strings from more documents they are not in the right order. It is something like this:
001a, 001b, 001d, 100a, 100c, 100d, 001c, 001f, 100b,...
I would like to get this:
001a, 001b, 001c, 001d, 100a, 001b, 100c, 100d, 001f,...
Using XSL 2.0:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:variable name="unsorted" select="'001a, 001b, 001d, 100a, 100c, 100d, 001c, 001f, 100b'"/>
<xsl:variable name="sorted">
<xsl:for-each select="tokenize($unsorted, ', ')">
<xsl:sort select="." />
<xsl:choose>
<xsl:when test="position()!=last()">
<xsl:value-of select="."/><xsl:text>, </xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/">
<unsorted><xsl:value-of select="$unsorted"/></unsorted>
<sorted><xsl:value-of select="$sorted"/></sorted>
</xsl:template>
</xsl:stylesheet>

How to programmatically iterate in XSLT across multiple files with similar name

A have following xmls:
data_0.xml
data_1.xml
data_3.xml
and so on...
And in xslt file I want to iterate through all files, so I tried for-each function.
<xsl:for-each select="document('data.xml')/*">
How to iterate on all of them? Add mask somehow? This surely won't work:
<xsl:for-each select="document('data_*.xml')/*">
Here is your solution in xslt 1.0:
I have four files in my filesystem:
Doc1.xml:
<p>Doc1</p>
Doc2.xml:
<p>Doc2</p>
Doc3.xml:
<p>Doc3</p>
Doc4.xml:
<p>Doc4</p>
and my xslt to get their output is:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Root>
<xsl:call-template name="getDocuments"/>
</Root>
</xsl:template>
<xsl:template name="getDocuments">
<xsl:param name="fileStartWith" select="'Doc'"/>
<xsl:param name="endCounter">4</xsl:param>
<xsl:param name="startCounter">1</xsl:param>
<xsl:choose>
<xsl:when test="$endCounter > 0">
<xsl:variable name="fileName"><xsl:value-of select="concat($fileStartWith,$startCounter,'.xml')"/></xsl:variable>
<xsl:for-each select="document($fileName)/*">
<xsl:copy-of select="."/><xsl:text>
</xsl:text>
</xsl:for-each>
<xsl:call-template name="getDocuments">
<xsl:with-param name="startCounter" select="$startCounter + 1"/>
<xsl:with-param name="fileStartWith" select="$fileStartWith"/>
<xsl:with-param name="endCounter" select="$endCounter - 1"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
generated output is:
<Root>
<p>Doc1</p>
<p>Doc2</p>
<p>Doc3</p>
<p>Doc4</p>
</Root>
Please make sure that xslt and xml is on the same path otherwise you need to change the content of document function.

Whitespace text manipulation

I have the following XML generated:
<InvoiceStreet>Rod House Rods Way Euro Industrial Estate</InvoiceStreet>
This contains white-space, I'd like to use xslt to transform it to the following:
<AddressLine>Rod House Rods Way</AddressLine>
<AddressLine>Euro Industrial Estate</AddressLine>
At the moment I'm only able to output it like so:
<AddressLine>Rod House Rods Way
Euro Industrial Estate</AddressLine>
Is there a way to do this using XSLT?
Edit
Here is what I currently have:
<Address>
<AddressLine>
<xsl:value-of select="/*/*/*/*/*/xsales:DeliveryStreet" />
</AddressLine>
</Address>
From the output that you posted, it appears that the input has the components of the <xsales:DeliveryAddress> address separated by a line-feed character.
If so, you can split xsales:DeliveryAddress on line feeds and put each line in it's own <AddressLine> using a recursive template that looks for a line-feed character, creates a new <AddressLine> for content before the line-feed and then processes the text after it with a subsequent call to the template:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsales="xsales"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mailplus="http://api.mailplus.nl/rss/mailplus/">
<xsl:output method="html" indent="yes"/>
<xsl:param name="ItemsToShow" select="2"/>
<xsl:param name="ShowItemDetails"/>
<xsl:param name="ShowItemDate"/>
<xsl:param name="Locale"/>
<xsl:param name="Target"/>
<xsl:template match="rss">
<Address>
<xsl:call-template name="AddressLines">
<xsl:with-param name="txt"
select="/*/*/*/*/*/xsales:DeliveryStreet"/>
</xsl:call-template>
</Address>
</xsl:template>
<xsl:template name="AddressLines">
<xsl:param name="txt"/>
<!--default value for delimiter to use line-feed character -->
<xsl:param name="delimiter" select="'
'"/>
<xsl:choose>
<xsl:when test="contains($txt, $delimiter)">
<AddressLine>
<xsl:value-of select="normalize-space(
substring-before($txt,
$delimiter))"/>
</AddressLine>
<xsl:call-template name="AddressLines">
<xsl:with-param name="txt"
select="substring-after($txt,
$delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!--filter out whitespace-only lines(from trailing line feeds)-->
<xsl:if test="normalize-space($txt)">
<AddressLine>
<xsl:value-of select="$txt"/>
</AddressLine>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Use substring Function, try using this piece of code snippet instead:
<Address>
<AddressLine>
<xsl:value-of select="substring(/*/*/*/*/*/xsales:DeliveryStreet,0,18)" />
</AddressLine>
<AddressLine>
<xsl:value-of select="substring(/*/*/*/*/*/xsales:DeliveryStreet,19,22)" />
</AddressLine>
</Address>
Better Approach
Get the length of the complete address. e.g. the length is 41 for the above provided sample string.
<xsl:variable name="addressLength" select="string-length(/*/*/*/*/*/xsales:DeliveryStreet)" />
Divide the length by 2 and truncate the floating part, i.e. you will get 20
<xsl:variable name="splitLength" select="$addressLength / 2" />
Now apply the substring function from zero (0) to splitLength variable
substring(/*/*/*/*/*/xsales:DeliveryStreet, 0, $splitLength)
e.g. you will get the output as "Rod House Rods Way E"
Again apply the substring function upto last occurance of space using LastIndexOf template.
e.g. you will get the desired output as "Rod House Rods Way"
For LastIndexOf template approach, you can refer to this post, XSLT: Finding last occurance in a string
Hope this will solve your problem for dynamic addresses, Cheers!

XSLT count comma values count

I have a value like integer="1,2,3,4,5" in the xml. How can I count the total number using XSLT. So that the output gives me a count of 5
Regards,
Sam
Here's one way (there may be others). Simply translate all commas into empty strings, and then compare in difference in length of strings:
<xsl:value-of
select="string-length(#integer)
- string-length(translate(#integer, ',', '')) + 1" />
If you need to handle empty strings, try this instead
<xsl:value-of
select="string-length(#integer)
- string-length(translate(#integer, ',', ''))
+ 1 * (string-length(#integer) != 0)" />
If you want to count the comma-separated-values, but ALSO be able to reference the individual items, you can use a recursive template like such.
This XSLT 1.0 style-sheet will convert the comma-separated-values into nodes and then count them ...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:variable name="as-nodes">
<xsl:call-template name="parse-comma-separated-values">
<xsl:with-param name="csv" select="t/#csv" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="count(msxsl:node-set($as-nodes)/*)" />
</xsl:template>
<xsl:template name="parse-comma-separated-values">
<xsl:param name="csv" />
<xsl:choose>
<xsl:when test="$csv = ''"/>
<xsl:when test="not( contains( $csv, ','))">
<value-node value="{$csv}" />
</xsl:when>
<xsl:otherwise>
<value-node value="{substring-before($csv,',')}" />
<xsl:call-template name="parse-comma-separated-values">
<xsl:with-param name="csv" select="substring-after($csv,',')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
... when applied to this input document ...
<t csv="1,2,3,4,5"/>
... produces ...
5

How can I replace certain characters with their escaped variants using XSLT?

I'm trying to develop an XSLT stylesheet which transforms a given DocBook document to a file which can be fed to the lout document formatting system (which then generates PostScript output).
Doing so requires that I replace a few characters in the text of DocBook elements because they have a special meaning to lout. In particular, the characters
/ | & { } # # ~ \ "
need to be enclosed in double quotes (") so that lout treats them as ordinary characters.
For instance, a DocBook element like
<para>This is a sample {a contrived one at that} ~ it serves no special purpose.</para>
should be transformed to
#PP
This is a sample "{"a contrived one at that"}" "~" it serves no special purpose.
How can I do this with XSLT? I'm using xsltproc, so using XPath 2.0 functions is not an option but a number of EXSLT functions are available.
I tried using a recursive template which yields the substring up to a special character (e.g. {), then the escaped character sequence ("{") and then calls itself on the substring after the special character. However, I have a hard time making this work properly when trying to replace multiple characters, and one of them is used in the escaped sequence itself.
In particular, the characters
/ | & { } # # ~ \ "
need to be enclosed in double quotes
(") so that lout treats them as
ordinary characters.
I. This is most easily accomplished using the str-map template of FXSL:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:strmap="strmap"
exclude-result-prefixes="xsl f strmap">
<xsl:import href="str-dvc-map.xsl"/>
<xsl:output method="text"/>
<strmap:strmap/>
<xsl:template match="/">
<xsl:variable name="vMapFun" select="document('')/*/strmap:*[1]"/>
#PP
<xsl:call-template name="str-map">
<xsl:with-param name="pFun" select="$vMapFun"/>
<xsl:with-param name="pStr" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="escape" match="strmap:*" mode="f:FXSL">
<xsl:param name="arg1"/>
<xsl:variable name="vspecChars">/|&{}##~\"</xsl:variable>
<xsl:variable name="vEscaping" select=
"substring('"', 1 div contains($vspecChars, $arg1))
"/>
<xsl:value-of select=
"concat($vEscaping, $arg1, $vEscaping)"/>
</xsl:template>
</xsl:stylesheet>
when this transformation is aplied on the provided XML document:
<para>This is a sample {a contrived one at that} ~ it serves no special purpose.</para>
the wanted, correct result is produced:
#PP
This is a sample "{"a contrived one at that"}" "~" it serves no special purpose.
II. With XSLT 1.0 recursive named template:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
#PP
<xsl:call-template name="escape">
<xsl:with-param name="pStr" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="escape">
<xsl:param name="pStr" select="."/>
<xsl:param name="pspecChars">/|&{}##~\"</xsl:param>
<xsl:if test="string-length($pStr)">
<xsl:variable name="vchar1" select="substring($pStr,1,1)"/>
<xsl:variable name="vEscaping" select=
"substring('"', 1 div contains($pspecChars, $vchar1))
"/>
<xsl:value-of select=
"concat($vEscaping, $vchar1, $vEscaping)"/>
<xsl:call-template name="escape">
<xsl:with-param name="pStr" select="substring($pStr,2)"/>
<xsl:with-param name="pspecChars" select="$pspecChars"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>