How to select and separate comma values in XSLT - xslt

My XML:
<geoCode>36.113,-114.925</geoCode>
I need a XSLT which converts the above XML to below XML format:
<geoCode>
<lati>36.113</lati>
<longi>-114.925</longi>
</geoCode>

You can use the following code in your template:
<xsl:template match="...">
<xsl:variable name="geo-code-split" select="tokenize(geoCode, ',')" />
<geoCode>
<lati><xsl:value-of select="$geo-code-split[1]" /></lati>
<longi><xsl:value-of select="$geo-code-split[2]" /></longi>
</geoCode>
</xsl:template>
P.S.: This solution uses XSLT 2.0. For XSLT 1.0, you can use the string-before() and string-after() functions.

<geoCode>
<lati>
<xsl:value-of select="substring-before(geoCode,',')"/>
</lati>
<longi>
<xsl:value-of select="substring-after(geoCode,',')"/>
</longi>
</geoCode>
This is with substring-before and substring-after.

Related

Can I not access my xsl:variable this way when I'm outputting plain text

I'm doing an xslt transform that generates a sql statement for me. The way I'm using below is not working. Is there a way?
<xsl:template match="foo">
<xsl:variable name="var1" select="#att_val1" />
select $var1.* from $var1
</xsl:template>
I know it will work if I do this:
<xsl:template match="foo">
select <xsl:value-of select="#att_val1" />.* from <xsl:value-of select="#att_val1" />
</xsl:template>
In XSLT 1.0, variable references are recognized in XPath expressions, but not in general template text. To evaluate an XPath expression and output the result as a text node in the result tree, use xsl:value-of, as you already know how to do. Example:
<xsl:template match="foo">
<xsl:variable name="var1" select="#att_val1" />
select <xsl:value-of select="$var1"/>.* from <xsl:value-of select="$var1"/>
</xsl:template>
Alternatively, you could build the whole select command in one xsl:value-of with use of the concat() function.
Unless you move to XSLT 3.0 (https://www.w3.org/TR/xslt-30/#text-value-templates) where you can do e.g. <xsl:template match="foo" expand-text="yes">select {#att_val1}.* from {#att_val1}</xsl:template> you will have to use your second option or perhaps a <xsl:template match="foo"><xsl:value-of select="concat('select ', #att_val1, '.* from ', #att_val1)"/></xsl:template>, but there is certainly no way in XSLT 1.0 to avoid the use of xsl:value-of completely.

string-to-codepoints(string) equivalent in XSLT 1.0

I need to get ASCII value of character and Convert ASCII code back to character if it satisfies certain conditions.
So I came across these functions:
string-to-codepoints(string)
and
codepoints-to-string((int,int,...))
provided in XSLT 2.0 (Or Rather XPATH 2.0) But unfortunately I need to use XSLT 1.0 for this task.
So My question is
Is there any equivalent of these functions in XSLT 1.0? If not Can we design it?
Can experts here help me in that?
Thanks in advance
It is possible to replace all characters with codepoints above 255 by "?" using pure XSLT 1.0 without extensions.
Define a variable
<xsl:variable name="upto255">
!"#$%.../01234...ABC...abc...úûüýþÿ</xsl:variable>
whose value is a string containing all the characters in the range 0..255 that are legal in XML.
Then use the double-translate trick:
<xsl:variable name="above255" select="translate($input, $upto255, '')"/>
This variable is a string containing all the non-Latin-1 characters present in the input string. Then use the recursive template
<xsl:template name="pad">
<xsl:param name="char"/>
<xsl:param name="count"/>
<xsl:choose>
<xsl:when test="$count=0"/>
<xsl:otherwise>
<xsl:value-of select="$char"/>
<xsl:call-template name="pad">
<xsl:with-param name="char" select="$char"/>
<xsl:with-param name="count" select="$count - 1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
to create a string of the right number of question marks:
<xsl:variable name="qqq">
<xsl:call-template name="pad">
<xsl:with-param name="char" select="'?'"/>
<xsl:with-param name="count" select="string-length($above255)"/>
</xsl:call-template>
</xsl:variable>
and then do the substitution:
<xsl:value-of select="translate($input, $above255, $qqq)"/>
But of course since you are in Java there is no excuse for writing all this XSLT 1.0 code which could be replaced by a single line of code if you switched to an XSLT 2.0 processor such as Saxon.
Based on your comments you want to perform a string replacement based on a regular expression. If you are using Java and Xalan then I think you can use e.g. java:replaceAll($inputString, $regExpPattern, $replacementString) to call the Java String method replaceAll, here is a simple example
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:java="http://xml.apache.org/xalan/java"
version="1.0"
exclude-result-prefixes="java">
<xsl:template match="/">
<xsl:value-of select="java:replaceAll('abc-123-def','\w+', '?')"/>
</xsl:template>
</xsl:stylesheet>
which outputs ?-?-? for me with Xalan.
On the other hand if you are using Java then you should consider moving to Saxon 9 and XSLT 2.0 as that way you can use the XPath 2.0 replace function (replace('abc-123-def', '\w+', '?')) without any need for extensions.
I am not sure what that has to do with your original question about string-to-codepoints and the ASCII code of characters.

Using an xsl param as argument to XPath function

I've been trying to figure out a way to use a param/variable as an argument to a function.
At the very least, I'd like to be able to use basic string parameters as arguments as follows:
<xsl:param name="stringValue" default="'abcdef'"/>
<xsl:value-of select="substring(string($stringValue),1,3)"/>
The above code generates no output.
I feel like I'm missing a simple way of doing this. I'm happy to use exslt or some other extension if an xslt 1.0 processor does not allow this.
Edit:
I am using XSL 1.0 and transforming using Nokogiri, which supports XPATH 1.0 . Here is a more complete snippet of what I am trying to do:
I want to pass column numbers as parameters using nokogiri as follows
document = Nokogiri::XML(File.read('table.xml'))
template = Nokogiri::XSLT(File.read('extractTableData.xsl'))
transformed_document = template.transform(document,
["tableName","'Problems'", #Table Heading
"tablePath","'Table'", #Takes an absolute XPATH String
"nameColumnIndex","2", #column number
"valueColumnIndex","3"]) #column number
File.open('FormattedOutput.xml', 'w').write(transformed_document)
My xsl then wants to access every TD[valueColumnIndex] and and retrieve the first 3 characters at that position, which is why I am using a substring function. So I want to do something like:
<xsl:value-of select="substring(string(TD[$valueColumnIndex]),1,3)"/>
Since I was unable to do that, I tried to extract TD[$valueColumnIndex] to another param valueCode and then do substring(string(valueCode),1,3)
That did not work either (which is to say, no text was output, whereas <xsl:value-of select="$valueCode"/> gave me the expected output).
As a result, i decided to understand how to use parameters better, I would just use a hard coded string, as mentioned in my earlier question.
Things I have tried:
using single quotes around abcdef (and not) while
using string() around the param name (and not)
Based on the comments below, it seems I am handicapped in my ability to understand the error because Nokogiri does not report an error for these situations. I am in the process of installing xsltproc right now and seeing if I receive any errors.
Finally, here is my entire xsl. I use a separate template forLoop because of the valueCode param I am creating. The lines of interest are the last 5 or so. I cannot include the xml as there are data use issues involved.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
xmlns:dyn="http://exslt.org/dynamic"
exclude-result-prefixes="ext dyn">
<xsl:param name="tableName" />
<xsl:param name="tablePath" />
<xsl:param name= "nameColumnIndex" />
<xsl:param name= "valueColumnIndex"/>
<xsl:template match="/">
<xsl:param name="tableRowPath">
<xsl:value-of select="$tablePath"/><xsl:text>/TR</xsl:text>
</xsl:param>
<!-- Problems -->
<section>
<name>
<xsl:value-of select="$tableName" />
</name>
<!-- <xsl:for-each select="concat($tablePath,'/TR')"> -->
<xsl:for-each select="dyn:evaluate($tableRowPath)">
<!-- Encode record section -->
<xsl:call-template name="forLoop"/>
</xsl:for-each>
</section>
</xsl:template>
<xsl:template name="forLoop">
<xsl:param name="valueCode">
<xsl:value-of select="./TD[number($valueColumnIndex)][text()]"/>
</xsl:param>
<xsl:param name="RandomString" select="'Try123'"/>
<section>
<name>
<xsl:value-of select="./TD[number($nameColumnIndex)]"/>
</name>
<code>
<short>
<xsl:value-of select="substring(string($valueCode),1,3)"/>
</short>
<long>
<xsl:value-of select="$valueCode"/>
</long>
</code>
</section>
</xsl:template>
</xsl:stylesheet>
Use it this way:
<xsl:param name="stringValue" select="'abcdef'"/>
<xsl:value-of select="substring($stringValue,1,3)"/>

how to convert xslt version 2.0 to version 1.0

i am using xslt document which uses xpath version 2 functions. I only have xalan 2.6 jar which has xslt 1.0 processor, its a constraint i cannot change it..please help me if there are any tools that convert xpath 2.0 functions to 1.0
<xsl:variable name="var1_ClinicalDocument" as="node()?" select="ns0:ClinicalDocument"/>
<DSMessage>
<DSPatient>
<xsl:for-each
select="$var1_ClinicalDocument[fn:exists(ns0:id/#root)]">
<Source>
<xsl:sequence select="fn:string(ns0:id/#root)"/>
</Source>
</xsl:for-each>
<Demographics>
<xsl:for-each select="($var1_ClinicalDocument/ns0:recordTarget/ns0:patientRole/ns0:id)[fn:exists(#extension)]">
<externalID>
<xsl:sequence select="fn:string(#extension)"/>
</externalID>
</xsl:for-each>
<xsl:for-each select="($var1_ClinicalDocument/ns0:recordTarget/ns0:patientRole/ns0:patient/ns0:name/ns0:given/node())[fn:boolean(self::text())]">
<firstName>
<xsl:sequence select="fn:string(.)"/>
</firstName>
Here is a mapping of the XPath 2.0 functions in question:
XPath 2.0 XPath 1.0 Xalan-Java XSLT-C
fn:exists() string-length(concat(., ) ) XPath 1.0 XPath 1.0
fn:string() string() XPath 1.0 XPath 1.0
fn:boolean() boolean() XPath 1.0 XPath 1.0
EXSLT, XSLTSL, FXSL, and XPath algorithms can fill in most of the remaining functionality.
XPath 2.0 FXSL XSLTSL EXSLT XPath 1.0
fn:max maximum math:max
fn:max dyn:max
fn:min minimum math:min
fn:min dyn:min
fn:sum sum math:sum sum
fn:sum dyn:sum
fn:avg sum div count
fn:floor floor
fn:ceiling ceiling
fn:round round
fn:abs math:abs
fn:collection foldl
op:concatenate append
fn:doc document
fn:count count
fn:not not
fn:true true
fn:false false
fn:boolean boolean
fn:upper-case str:to-upper
fn:lower-case str:to-lower
fn:substring substring
fn:string-length string-length
fn:normalize-space normalize-space
fn:translate translate
fn:concat str:concat concat
fn:substring-before substring-before
fn:substring-after substring-after
fn:reverse str:backward
fn:insert-before str:insert-at
fn:matches str:string-match
fn:tokenize str:tokenize
fn:resolve-uri uri:resolve-uri
fn:distinct-values set:distinct
op:union |
op:intersect set:intersection
op:except cmp:diff set:difference
op:is-same-node cmp:cmp
fn:position node:xpath position
fn:last last
fn:data node:type
fn:lang lang
fn:current-dateTime date:date-time
fn:dateTime dt:format-date-time date:format-date
fn:year-from-date dt:get-xsd-datetime-year date:day-in-year
fn:month-from-dateTime dt:get-xsd-datetime-month date:month-name
fn:day-from-dateTime dt:get-xsd-datetime-day date:day-name
fn:hours-from-dateTime dt:get-xsd-datetime-hour date:hour-in-day
fn:minutes-from-dateTime dt:get-xsd-datetime-minute date:minute-in-hour
fn:seconds-from-dateTime dt:get-xsd-datetime-second date:second-in-minute
fn:timezone-from-dateTime dt:get-xsd-datetime-timezone
if (...) then (...) else(...) $dynamic[$var] | $default[not($var)]
References
Xalan-Java Extensions Library
XSLTC Extensions Library
FXSL
XSieve
XSLTSL
Common XSLT Utilities
Web XSLT: XSLT and JavaScript code intended mostly for manipulating MathML and OpenMath.
XSLT Stylesheets for MathML
XPath Wikibook
XQuery 1.0 and XPath 2.0 Functions and Operators (Second
Edition)
XQuery 1.0 and XPath 2.0 Formal Semantics (Second Edition)
XQuery 1.0 and XPath 2.0 Data Model (XDM) (Second Edition)
I will try, but this is untested:
<xsl:variable name="var1_ClinicalDocument" select="ns0:ClinicalDocument"/>
<DSMessage>
<DSPatient>
<xsl:for-each
select="$var1_ClinicalDocument[ns0:id/#root]">
<Source>
<xsl:value-of select="ns0:id/#root"/>
</Source>
</xsl:for-each>
<Demographics>
<xsl:for-each select="($var1_ClinicalDocument/ns0:recordTarget/ns0:patientRole/ns0:id)[#extension]">
<externalID>
<xsl:value-of select="#extension"/>
</externalID>
</xsl:for-each>
<xsl:for-each select="($var1_ClinicalDocument/ns0:recordTarget/ns0:patientRole/ns0:patient/ns0:name/ns0:given/node())[self::text()]">
<firstName>
<xsl:value-of select="."/>
</firstName>
Basically the use of fn:exists, fn:string and fn:boolean can be replaced, of course if there is use of XPath/XSLT 2.0 stuff like tokenize or for-each-group you need more work and maybe Xalan specific extension functions.
This should be a not-bad translation:
<xsl:variable name="var1_ClinicalDocument" select="ns0:ClinicalDocument"/>
<DSMessage>
<DSPatient>
<xsl:for-each select="$var1_ClinicalDocument[ns0:id/#root]">
<Source>
<xsl:value-of select="ns0:id/#root"/>
</Source>
</xsl:for-each>
<Demographics>
<xsl:for-each select=
"($var1_ClinicalDocument/ns0:recordTarget
/ns0:patientRole/ns0:id)[#extension]">
<externalID>
<xsl:value-of select="concat(#extension, ' ')"/>
</externalID>
</xsl:for-each>
<xsl:for-each select=
"$var1_ClinicalDocument/ns0:recordTarget/ns0:patientRole
/ns0:patient/ns0:name/ns0:given/text()">
<firstName>
<xsl:value-of select="."/>
</firstName>
</xsl:for-each>
</Demographics>
</DSPatient>
</DSMessage>

Retrieve page URL params or page URL in XSLT

I have a page which has URL structure :
http://www.abc.com/xyz?parama=1&paramb=2
Is is possible to create a generic method for getting the values of any additional params the URL maybe (parama=1&paramb=2)
Is is possible to get the URL of the page in XSL similar to javascript's location.href ?
Is is possible to get the URL of the page in XSL similar to javascript's location.href ?
Not exactly in the same way, but yes, the query string can be passed as a parameter.
Is is possible to create a generic
method for getting the values of any
additional params the URL maybe
(parama=1&paramb=2)
Yes, one can perform tokenization (with the tokenize() function in XSLT 2.0 or in XSLT 1.0 using the str-split-to-words template of **FXSL 1.x or a self-written recursive tokenization template.)
XSLT 1.0 solution:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
>
<xsl:import href="strSplit-to-Words.xsl"/>
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:param name="pQString" select=
"'?parama=1&paramb=2&anyParamName=AnyValue'"
/>
<xsl:template match="/">
<xsl:variable name="vwordNodes">
<xsl:call-template name="str-split-to-words">
<xsl:with-param name="pStr" select="$pQString"/>
<xsl:with-param name="pDelimiters"
select="'?&'"/>
</xsl:call-template>
</xsl:variable>
<xsl:apply-templates select=
"ext:node-set($vwordNodes)/*
"/>
</xsl:template>
<xsl:template match="word">
<xsl:value-of select="."/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
when the above transformation is applied on any XML document (will not be used), the wanted result is produced:
parama=1
paramb=2
anyParamName=AnyValue
Do note the use of the FXSL 1.x str-split-to-words template and the use of the EXSLT ext:node-set() extension function.
XSLT 2.0 solution:
<xsl:param name="pQString" as="xs:string" select=
"'?parama=1&paramb=2&anyParamName=AnyValue'"
/>
<xsl:template match="/">
<xsl:value-of separator="
" select=
"tokenize($pQString, '\?|&')
"/>
</xsl:template>
When the above XSLT 2.0 transformation is performed, it produces the correct result:
parama=1
paramb=2
anyParamName=AnyValue
This might be possible using EXSLT, or registering a function callback with your particular xslt processor, but not with plain old XSLT, at least not to my knowledge. With plain XSLT, if it isn't in the XML, then it doesn't exist.
If it is client side, there isn't any way of doing this. Unfortunately, xsl is very limited in the browser. I'm afraid you will need to move the functionality to the webapp or to javascript.
If the transform is server side, there might be something you can do.
I workaround this by using javascript in mi XSLT file. take a look.
on The XML I have a tag element named tag! yes, very original...
<tag url="http://www.demo.net/share.php?u=param1 />
Mi Sample transform
<div class="HelloDiv">
<xsl:for-each select="tag">
<a href="{#url}">
This is my custom URL
</a>
</xsl:for-each>
</div>
Now inside the template of the transform, in going to load my custom value in Param1
in this case i'm going to use the document.title.. by using a jquery function.
<script type="text/javascript">
jQuery(function(){
jQuery('div.HelloDiv a').each(function(){
var parameter = jQuery(this).attr('href');
parameter = currentUrl.replace('param1',escape(document.title));
jQuery(this).attr('href',parameter);
})
});
</script>