Using XSLT, how to translate " with ' - xslt

Using XSLT, I need to create a CSV report while processing an XML document.
During the procedure, I check if one of the column will contain a comma (,); if yes I put quotes around the value. My problem is that the value may already contains " ("), which will confuse the CSV format.
In XSLT, how can I replace all the " (") in a string with ' (')
I tried using fn:translate:
<xsl:value-of select="**fn:translate(., '"', '#apos;')**"/>
is rejected because it sees it as
<xsl:value-of select="fn:translate(., '"', **'''**)"/>
Any suggestion?
Current XSLT
<xsl:function name="cm:forCSV">
<xsl:param name="node" as="item()*"/>
<xsl:if test="contains($node, ',')">
<xsl:text>"</xsl:text>
</xsl:if>
<xsl:choose>
<xsl:when test="$node instance of xs:string">
<xsl:value-of select="fn:normalize-space(**fn:translate($node, '"', '&apos;')**)"/>
</xsl:when>
<xsl:otherwise>
...
</xsl:otherwise>
</xsl:choose>
<xsl:if test="contains($node, ',')">
<xsl:text>"</xsl:text>
</xsl:if>
</xsl:function>
Sample data:
<sample value="test">an example**,** to be saved in a column
Expected:
<sample value='test'>an example**,** to be saved in a column
To have in my CSV
..., "<sample value='test'>an example**,** to be saved in a column", ...

Since you are using XSLT 2.0 or higher, you can escape apostrophes and quotes by doubling them:
<xsl:value-of select="translate($string, '"', '&apos;&apos;')" />
From the XPath 2.0 specification:
If the literal is delimited by apostrophes, two adjacent apostrophes within the literal are interpreted as a single apostrophe. Similarly, if the literal is delimited by quotation marks, two adjacent quotation marks within the literal are interpreted as one quotation mark.
In XSLT 1.0, you would have to do:
<xsl:variable name="apos" select='"&apos;"'/>
<xsl:value-of select="translate($string, '"', $apos)" />

Another option for referring to quotes, apostrophes, ampersands, etc, in XSLT is to use the codepoints-to-string function to generate them:
<xsl:value-of select="translate(., codepoints-to-string(34), codepoints-to-string(39)"/>
... or define variables using ' to enclose " and vice versa:
<xsl:variable name="quote" select='"'/>
<xsl:variable name="apos" select="'"/>
... or using a text node to define the value:
<xsl:variable name="quote">"</xsl:variable>
<xsl:variable name="apos">'</xsl:variable>
... and then refer to the variables:
<xsl:value-of select="translate(., $quote, $apos)"/>
But actually I'd suggest that rather than translate " to ', you could use a regular expression to find " characters in the input string and double them (i.e. replace each " with "") which is the standard way to include " characters in a quoted CSV field.
<xsl:variable name="quote-regex">/"</xsl:variable>
<xsl:value-of select="replace(., $quote-regex, '$1$1')"/>

In XPath, you can use single quotes to quote a double quote and vice versa:
fn:translate(., '"', "'")
When the XPath expression is the select attribute of an XML element, you can put it in single quotes and escape the single quotes in the expression as &apos;, or similarly put it in double quotes:
<xsl:value-of select="fn:translate(., '"', "'")"/>

Related

how to convert comma separated values to new line separated values in XSL

i have comma separated in that i want to convert them to line separated values
sample :
11,22,33,44,55
i want to convert them to
11
22
33
44
I've tried this function
oraext:create-nodeset-from-delimited-string
but it adds space between values instead of new line break .
using xsl.
is there any function that make desired output
In XSLT-1.0 you can use the fn:translate(...) function to convert all commas to newlines:
<xsl:value-of select="translate(value,',','
')" />
This doesn't remove leading and trailing spaces between the commas.
In XSLT-2.0, on the other hand, you can also remove those with a regular expression and the fn:replace(...) function:
<xsl:value-of select="replace(value,'\s*,\s*','
')" />
If you try to create HTML output like mentioned in the comment above, you can use this recursive template to finish each number with a <BR /> tag:
<xsl:template match="/*" name="strItem">
<xsl:param name="str" select="concat(value,',')" />
<xsl:value-of select="concat(normalize-space(substring-before($str,',')),'<br />')" disable-output-escaping="yes" />
<xsl:if test="normalize-space(substring-after($str,','))">
<xsl:call-template name="strItem">
<xsl:with-param name="str" select="substring-after($str,',')" />
</xsl:call-template>
</xsl:if>
</xsl:template>
With a <value>11,22,33,44,55</value> element (not at the root level), the output is
11<br />22<br />33<br />44<br />55<br />

How to preserve the " in an inbound XML file through XSLT and ANT

This problem has been driving me crazy today.
(note I've added spaced to stop the characters being encoded below)
In an input XML file I have html text like "& lt;li class=& quot;classname& quot;& gt;"
The input file is correctly encoded for our needs.
The trouble is I'm running it though an XSLT to transform the XML into a CSV. Everything was okay for the & lt; & gt;, but the XSLT is outputting the & quot; as '"'. So I finally resorted to the xslt segment below to try to replace the embedded quotes back to & quot;.
I couldn't figure out how to get the xslt to replace the quotes with & quot;, so, because I'm already using ant for the xslt processing, I thought it would be easier to use ANT replace to replace the '\"' with & quot; after the xslt task.
Now I'm going crazy, because ANT is also expanding & quot; and replacing the \" with "!!!
So parts to the question:
1. Is there a way to preserve the & quot; during the xslt transform. (best)
2. If not is there a way to string replace the " with & quot; from the xslt?
3. If neither of them work, what's the syntax to get ANT to output & quot; instead of " (i.e escape the entity)
ANT
<replace file="${dataload.dataDir}/inbound/products/wc_store_locator.csv" token='\"' value=""" />
XSLT
<xsl:call-template name="replace-quotes">
<xsl:with-param name="text" select="notificationMessage"/>
<xsl:with-param name="replace" select="'"'" />
<xsl:with-param name="with" select="'\"'"/>
</xsl:call-template>
<xsl:text>"</xsl:text>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template name="replace-quotes">
<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-quotes">
<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>
ah never mind, I figured out the ant syntax:
it was:
& amp;quot;
Doing a replace of " by " using a process that isn't XML-aware seems crazy - it will replace all the attribute-delimiting quotes as well as those appearing within text content.
When I see this kind of question my immediate reaction is to challenge the requirement. Why are you trying to do this? Why do you need quotes to be escaped, when an XML parser doesn't require them to be escaped? Are you reading the XML with something that isn't a true XML parser, and if so why? If you don't want to use XML properly, is it wise to use it at all?

Create a list/array in XSLT

I have the following scenario. I have a list of countries (EG, KSA, UAE, AG)
I need to check an XML input if it is contained in this list or not:
<xsl:variable name="$country" select="Request/country" >
<!-- now I need to declare the list of countries here -->
<xsl:choose>
<!-- need to check if this list contains the country -->
<xsl:when test="$country='??????'">
<xsl:text>IN</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>OUT</xsl:text>
</xsl:otherwise>
</xsl:choose>
Note: I am using XSLT 1.0.
<xsl:variable name="$country" select="Request/country"/>
<xsl:variable name="countries">|EG|KSA|UAE|AG|</xsl:variable>
<xsl:when test="contains($countries,$country)">...</xsl:when>
EDIT
Upon reading your post again, I think the original version of my answer (below) is not it.
You have a list already - your variable declaration selects a node-set of all <country> nodes that are children of <Request> (a node-set is the XSLT equivalent of an array/a list):
<xsl:variable name="$country" select="Request/country" >
But the point is, you don't even need that list as a separate variable; all you need is:
<xsl:when test="Request[country=$country]"><!-- … --></xsl:when>
Where Request[country=$country] reads as "Within <Request>, look at every <country> and select it if it is equal to $country." When the expression returns a non-empty node-set, $country is in the list.
Which is, in fact, what Rubens Farias said from the start. :)
Original answer, kept for the record.
If by "list" you mean a comma-separated string of tokens:
<!-- instead of a variable, this could be a param or dynamically calculated -->
<xsl:variable name="countries" select="'EG, KSA, UAE, AG'" />
<xsl:variable name="country" select="'KSA'" />
<xsl:choose>
<!-- concat the separator to start and end to ensure unambiguous matching -->
<xsl:when test="
contains(
concat(', ', normalize-space($countries), ', ')
concat(', ', $country, ', ')
)
">
<xsl:text>IN</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>OUT</xsl:text>
</xsl:otherwise>
</xsl:choose>
Try something like, if your country list belongs on your XML input:
<xsl:when test="/yourlist[country = $country]'">
Or, if that's static, you could go with:
<xsl:when test="$country = 'EG' or $country = 'KSA' or ...">
I am modifying sam's answer,
you can create a variable which is pipe separated as a string (not a list but act as list), country codes are unique so contains method works fine
but for the similar name variables consider 'Product', 'ProductType' and 'ProductItem' present as countries parameter then it will not work ie if condition fails as contains method check string is present in the provided string,
So my suggestion is to add separators also along with the name ie concat to work contains as search criteria.
<xsl:variable name="$country" select="Request/country"/>
<xsl:variable name="countries">|EG|KSA|UAE|AG|</xsl:variable>
<xsl:when test="contains($countries,concat('|',$country,'|')">
your logic goes here....
</xsl:when>

Removing non-alphanumeric characters from string in XSL

How do I remove non-alphanumeric characters from a string in XSL?
If you define non-alphanumeric as [^a-zA-Z0-9]:
<xsl:value-of select="
translate(
string,
translate(
string,
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
''
),
''
)
" />
Note that this is for XSLT 1.0. In XSLT 2.0 you can work with regexes directly, using replace().
For XSLT 2.0 you can use replace() as follows:
<xsl:value-of select="replace(<string>, '[^a-zA-Z0-9]', '')" />

How to implement Carriage return in XSLT

I want to implement carriage return within xslt.
The problem is I have a varible:
Step 1 = Value 1 breaktag Step 2 = Value 2 as a string and would like to appear as
Step 1 = Value 1
Step 2 = Value 2
in the HTML form but I am getting the br tag on the page.Any good ways of implementing a line feed/carriage return in xsl would be appreciated
As an alternative to
<xsl:text>
</xsl:text>
you could use
<xsl:text>
</xsl:text> <!-- newline character -->
or
<xsl:text>
</xsl:text> <!-- carriage return character -->
in case you don't want to mess up your indentation
This works for me, as carriage-return + life feed.
<xsl:text>
</xsl:text>
The "
" string does not work.
The cleanest way I've found is to insert !ENTITY declarations at the top of the stylesheet for newline, tab, and other common text constructs. When having to insert a slew of formatting elements into your output this makes the transform sheet look much cleaner.
For example:
<?xml version="1.0"?>
<!DOCTYPE xsl:stylesheet [
<!ENTITY nl "<xsl:text>
</xsl:text>">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="step">
&nl;&nl;
<xsl:apply-templates />
</xsl:template>
...
</xsl:stylesheet>
use a simple carriage return in a xsl:text element
<xsl:text>
</xsl:text>
Try this at the end of the line where you want the carriage return. It worked for me.
<xsl:text><![CDATA[<br />]]></xsl:text>
I was looking for a nice solution to this, as many would prefer, without embedding escape sequences directly in the expressions, or having weird line breaks inside of a variable. I found a hybrid of both this approaches actually works, by embedding a text node inside a variable like this:
<xsl:variable name="newline"><xsl:text>
</xsl:text></xsl:variable>
<xsl:value select="concat(some_element, $newline)" />
Another nice side-affect of this is that you can pass in whatever newline you want, be it just LF, CR, or both CRLF.
--Daniel
Here is an approach that uses a recursive template, which looks for
in the string from the database and then outputs the substring before.
If there is a substring after
remaining, then the template calls itself until there is nothing left.
In case
is not present then the text is simply output.
Here is the template call (just replace #ActivityExtDescription with your database field):
<xsl:call-template name="MultilineTextOutput">
<xsl:with-param name="text" select="#ActivityExtDescription" />
</xsl:call-template>
and here is the code for the template itself:
<xsl:template name="MultilineTextOutput">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '
')">
<xsl:variable name="text-before-first-break">
<xsl:value-of select="substring-before($text, '
')" />
</xsl:variable>
<xsl:variable name="text-after-first-break">
<xsl:value-of select="substring-after($text, '
')" />
</xsl:variable>
<xsl:if test="not($text-before-first-break = '')">
<xsl:value-of select="$text-before-first-break" /><br />
</xsl:if>
<xsl:if test="not($text-after-first-break = '')">
<xsl:call-template name="MultilineTextOutput">
<xsl:with-param name="text" select="$text-after-first-break" />
</xsl:call-template>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" /><br />
</xsl:otherwise>
</xsl:choose>
Works like a charm!!!
I believe that you can use the xsl:text tag for this, as in
<xsl:text>
</xsl:text>
Chances are that by putting the closing tag on a line of its own, the newline is part of the literal text and outputted as such.
I separated the values by Environment.NewLine and then used a pre tag in html to emulate the effect I was looking for
This is the only solution that worked for me. Except I was replacing
with \r\n