Split a string in xslt - xslt

I need to split a string "ab|bc|cd>xy|yz|zx>pq|rs|tu>
help me if any one can...
here is the code....
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" indent="yes" />
<xsl:template match="/products/product">
<xsl:variable name="ImageString" select="properties/property[#name='Brand']"></xsl:variable>
<xsl:variable name="ImageFolderName" select="substring-before($ImageString,'#')" ></xsl:variable>
Folder Name:-<xsl:value-of select="$ImageFolderName" ></xsl:value-of>
<br/>
<xsl:variable name="ImageStringName" select="substring-after($ImageString,'#')" ></xsl:variable>
Final String:-<xsl:value-of select="$ImageStringName" ></xsl:value-of>
<xsl:variable name="values">
<xsl:text><xsl:value-of select="$ImageStringName" ></xsl:value-of></xsl:text>
</xsl:variable>
<xsl:call-template name="str:split">
<xsl:with-param name="string" select="$values" />
<xsl:with-param name="pattern" select="'|'" />
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>

To summarise the linked question below:
If you are using XSLT 2.0 then you can use tokenize(string, separator) method.
If you are using XSLT 1.0 then you'd need to write a recursive method to achieve this.
or
Use the tokenize() method if your template supports EXSLT.
See this question for full details of the options.

Related

number format for .csv using xslt1.0

I have tag in xml which value sometimes -ve number and sometimes +ve number and I want to show the -ve(e.g -10) number as (10) I mean with bracket using xslt1.0.
Could you please help me?
I am using below template but it is not working.
<xsl:call-template name="fmtNumber">
<xsl:with-param name="amt" select="retailer:Amount" />
</xsl:call-template>
<xsl:template name="fmtNumber">
<xsl:param name="amt" />
<xsl:value-of
select="format-number($amt, ' $###,##0 ;($###,##0)')" />
</xsl:template>
it is showing the result in csv file as $(10) but, I want (10), I mean without dollar sumbol.
do:
<xsl:value-of select="format-number($amt, '###,##0;(###,##0)')"/>
Please run the following stylesheet (with any XML source) on your processor and post the resulting code.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="num" select="-123" />
<xsl:template match="/">
<result>
<version><xsl:value-of select="system-property('xsl:version')"/></version>
<vendor><xsl:value-of select="system-property('xsl:vendor')"/></vendor>
<test><xsl:value-of select="format-number($num, '#,##0;(#,##0)')"/></test>
</result>
</xsl:template>
</xsl:stylesheet>

XSLT: Replace string with Abbreviations

I would like to know how to replace the string with the abbreviations.
My XML looks like below
<concept reltype="CONTAINS" name="Left Ventricular Major Axis Diastolic Dimension, 4-chamber view" type="NUM">
<code meaning="Left Ventricular Major Axis Diastolic Dimension, 4-chamber view" value="18074-5" schema="LN" />
<measurement value="5.7585187646">
<code value="cm" schema="UCUM" />
</measurement>
<content>
<concept reltype="HAS ACQ CONTEXT" name="Image Mode" type="CODE">
<code meaning="Image Mode" value="G-0373" schema="SRT" />
<code meaning="2D mode" value="G-03A2" schema="SRT" />
</concept>
</content>
</concept>
and I am selecting some value from the xml like,
<xsl:value-of select="concept/measurement/code/#value"/>
Now what I want is, I have to replace cm with centimeter. I have so many words like this. I would like to have a xml for abbreviations and replace from them.
I saw one similar example here.
Using a Map in XSL for expanding abbreviations
But it replaces node text, but I have text as attribute. Also, it would be better for me If I can find and replace when I select text using xsl:valueof select instead of having a separate xsl:template. Please help. I am new to xslt.
I have created XSLT v "1.1". For abbreviations I have created XML file as you have mentioned:
Abbreviation.xml:
<Abbreviations>
<Abbreviation>
<Short>cm</Short>
<Full>centimeter</Full>
</Abbreviation>
<Abbreviation>
<Short>m</Short>
<Full>meter</Full>
</Abbreviation>
</Abbreviations>
XSLT:
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" />
<xsl:param name="AbbreviationDoc" select="document('Abbreviation.xml')"/>
<xsl:template match="/">
<xsl:call-template name="Convert">
<xsl:with-param name="present" select="concept/measurement/code/#value"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="Convert">
<xsl:param name="present"/>
<xsl:choose>
<xsl:when test="$AbbreviationDoc/Abbreviations/Abbreviation[Short = $present]">
<xsl:value-of select="$AbbreviationDoc/Abbreviations/Abbreviation[Short = $present]/Full"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$present"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
INPUT:
as you have given <xsl:value-of select="concept/measurement/code/#value"/>
OUTPUT:
centimeter
You just need to enhance this Abbreviation.xml to keep short and full value of abbreviation and call 'Convert' template with passing current value to get desired output.
Here a little shorter version:
- with abbreviations in xslt file
- make use of apply-templates with mode to make usage shorter.
But with xslt 1.0 node-set extension is required.
<?xml version="1.0" encoding="utf-8"?>
<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:output method="xml" indent="yes"/>
<xsl:variable name="abbreviations_txt">
<abbreviation abbrev="cm" >centimeter</abbreviation>
<abbreviation abbrev="m" >meter</abbreviation>
</xsl:variable>
<xsl:variable name="abbreviations" select="exsl:node-set($abbreviations_txt)" />
<xsl:template match="/">
<xsl:apply-templates select="concept/measurement/code/#value" mode="abbrev_to_text"/>
</xsl:template>
<xsl:template match="* | #*" mode="abbrev_to_text">
<xsl:variable name="abbrev" select="." />
<xsl:variable name="long_text" select="$abbreviations//abbreviation[#abbrev = $abbrev]/text()" />
<xsl:value-of select="$long_text"/>
<xsl:if test="not ($long_text)">
<xsl:value-of select="$abbrev"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

xsl: passing variable into xsl file path

Is there a way pass a attribute, variable combinations directly into the path of the url with xsl?
Example:
http://something.xsl?asdf=12&attribute2=1234
I would like to use these attributes and values to enable certain flags inside the xsl file.
Use concat()
<xsl:variable name="url" select="concat($currURL, 'flag=true')" />
Yeah, you just need to escape the & as &
I think you mean can the stylesheet access its own URI to access the parameters. In XSLT2 you can use the static-base-uri() function to access the URI, and then you can split it up to extract the query parameters using the regex string functions. In XSLT1 it is not possible, you would need to pass the information in as stylesheet parameters, and XSLT1 stylesheet has no access to the URI of the source or itself.
Here is a complete XSLT 1.0 solution, assuming that the URL is passed as an external parameter to the transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pUrl" select=
"'http://something.xsl?asdf=12&attribute2=1234'"/>
<xsl:template match="/">
<xsl:variable name="vQuery" select="substring-after($pUrl, '?')"/>
<xsl:variable name="vrtfQueryItems">
<xsl:call-template name="buildQueryItems">
<xsl:with-param name="pQuery" select="$vQuery"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="vQueryItems"
select="ext:node-set($vrtfQueryItems)/*"/>
<xsl:copy-of select="$vQueryItems"/>
</xsl:template>
<xsl:template name="buildQueryItems">
<xsl:param name="pQuery"/>
<xsl:if test=
"string-length($pQuery) > 0">
<xsl:variable name="vQuery" select="concat($pQuery, '&')"/>
<xsl:variable name="vItem" select="substring-before($vQuery, '&')"/>
<param name="{substring-before(concat($vItem, '='), '=' )}">
<xsl:value-of select="substring-after($vItem, '=')"/>
</param>
<xsl:call-template name="buildQueryItems">
<xsl:with-param name="pQuery" select="substring-after($pQuery, '&')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used), the wanted result is produced:
<param name="asdf">12</param>
<param name="attribute2">1234</param>

XSLT: need to replace document('')

I've the following xslt file:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- USDomesticCountryList - USE UPPERCASE LETTERS ONLY -->
<xsl:variable name="USDomesticCountryList">
<entry name="US"/>
<entry name="UK"/>
<entry name="EG"/>
</xsl:variable>
<!--// USDomesticCountryList -->
<xsl:template name="IsUSDomesticCountry">
<xsl:param name="countryParam"/>
<xsl:variable name="country" select="normalize-space($countryParam)"/>
<xsl:value-of select="normalize-space(document('')//xsl:variable[#name='USDomesticCountryList']/entry[#name=$country]/#name)"/>
</xsl:template>
</xsl:stylesheet>
I need to replace the "document('')" xpath function, what should I use instead?
I've tried to remove it completely but the xsl document doesn't work for me!
I need to to so because the problem is :
I am using some XSLT document that uses the above file, say document a.
So I have document a that includes the above file (document b).
I am using doc a from java code, I am do Caching for doc a as a javax.xml.transform.Templates object to prevent multiple reads to the xsl file on every transformation request.
I found that, the doc b is re-calling itself from the harddisk, I believe this is because of the document('') function above, so I wanna replace/remove it.
Thanks.
If you want to access the nodes inside a variable you normally use the node-set() extension function. The availability and syntax depends on the processor you use. For MSXML and Saxon you can use exsl:node-set(). To use the extension function you will have to include the namespace that defines the function.
E.g. (tested wiht MSXML, returns US for countryName = 'US'):
<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:output method="xml"/>
<!-- USDomesticCountryList - USE UPPERCASE LETTERS ONLY -->
<xsl:variable name="USDomesticCountryList">
<entry name="US"/>
<entry name="UK"/>
<entry name="EG"/>
</xsl:variable>
<!--// USDomesticCountryList -->
<xsl:template name="IsUSDomesticCountry">
<xsl:param name="countryParam"/>
<xsl:variable name="country" select="normalize-space($countryParam)"/>
<xsl:value-of select="exsl:node-set($USDomesticCountryList)/entry[#name=$country]/#name"/>
</xsl:template>
</xsl:stylesheet>
If you're trying to make the IsUSDomesticCountry template work without using document(''), you could rewrite the template to
<xsl:template name="IsUSDomesticCountry">
<xsl:param name="countryParam"/>
<xsl:variable name="country" select="normalize-space($countryParam)"/>
<xsl:choose>
<xsl:when test="$country='US'">true</xsl:when>
<xsl:when test="$country='UK'">true</xsl:when>
<xsl:when test="$country='EG'">true</xsl:when>
<xsl:otherwise>false</xsl:otherwise>
</xsl:choose>
</xsl:template>
or
<xsl:template name="IsUSDomesticCountry">
<xsl:param name="countryParam"/>
<xsl:variable name="country" select="normalize-space($countryParam)"/>
<xsl:value-of select="$country='US' or $country='UK' or $country='EG'"/>
</xsl:template>
or even
<xsl:template name="IsUSDomesticCountry">
<xsl:param name="countryParam"/>
<xsl:variable name="country"
select="concat('-', normalize-space($countryParam),'-')"/>
<xsl:variable name="domesticCountries" select="'-US-UK-EG-'"/>
<xsl:value-of select="contains($domesticCountries, $country)"/>
</xsl:template>
Personally, I find the variant using document('') to be more readable.

Multiply 2 numbers and then sum

I am having a difficult time trying to do something that seems like it should be really easy to do. I basically want to multiply 2 numbers in a node and then sum the total of those numbers for all the nodes. Here is the XSLT code I have tried.
<xsl:value-of select="sum(Parts/Part/Quantity * Parts/Part/Rate)"/>
This code results in an error that says "Argument 1 of function sum cannot be converted to a node set."
Does anyone have an idea of what is wrong or how I can accomplish what I am trying to do?
Here are three possible solutions:
Solution1 XSLT2:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:sequence select="sum(/*/*/(rate * quantity))"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<parts>
<part>
<rate>0.37</rate>
<quantity>10</quantity>
</part>
<part>
<rate>0.03</rate>
<quantity>10</quantity>
</part>
</parts>
The wanted result is produced:
4
The XSLT 2.0 solution uses the fact that in XPath 2.0 it is allowed that the right argument of the last "/" operator can be an expression or generally a function. This expression/function is applied for each of the nodes selected so far acting as the context node, and each function application produces one result.
Solution2 XSLT 1.0:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:call-template name="sumProducts">
<xsl:with-param name="pList" select="*/*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="sumProducts">
<xsl:param name="pList"/>
<xsl:param name="pAccum" select="0"/>
<xsl:choose>
<xsl:when test="$pList">
<xsl:variable name="vHead" select="$pList[1]"/>
<xsl:call-template name="sumProducts">
<xsl:with-param name="pList" select="$pList[position() > 1]"/>
<xsl:with-param name="pAccum"
select="$pAccum + $vHead/rate * $vHead/quantity"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$pAccum"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When applied on the above XML document, the correct result is produced:
4
This is a typical XSLT 1.0 recursive solution. Do note how the sumProducts template calls itself recursively, until the entire input list, passed in the parameter $pList is processed.
Solution3 FXSL (XSLT 1.0):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
xmlns:test-map-product="test-map-product"
exclude-result-prefixes="xsl ext test-map-product"
>
<xsl:import href="sum.xsl"/>
<xsl:import href="map.xsl"/>
<xsl:import href="product.xsl"/>
<!-- This transformation is to be applied on:
salesMap.xml
It contains the code of the "sum of products" from the
article "The Functional Programming Language XSLT"
-->
<test-map-product:test-map-product/>
<xsl:output method="text"/>
<xsl:template match="/">
<!-- Get: map product /sales/sale -->
<xsl:variable name="vSalesTotals">
<xsl:variable name="vTestMap" select="document('')/*/test-map-product:*[1]"/>
<xsl:call-template name="map">
<xsl:with-param name="pFun" select="$vTestMap"/>
<xsl:with-param name="pList1" select="/sales/sale"/>
</xsl:call-template>
</xsl:variable>
<!-- Get sum map product /sales/sale -->
<xsl:call-template name="sum">
<xsl:with-param name="pList" select="ext:node-set($vSalesTotals)/*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="makeproduct" match="*[namespace-uri() = 'test-map-product']">
<xsl:param name="arg1"/>
<xsl:call-template name="product">
<xsl:with-param name="pList" select="$arg1/*"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<sales>
<sale>
<price>3.5</price>
<quantity>2</quantity>
<Discount>0.75</Discount>
<Discount>0.80</Discount>
<Discount>0.90</Discount>
</sale>
<sale>
<price>3.5</price>
<quantity>2</quantity>
<Discount>0.75</Discount>
<Discount>0.80</Discount>
<Discount>0.90</Discount>
</sale>
</sales>
The correct result is produced:
7.5600000000000005
In the last case for each sale we calculate the product of price, quantity and all available (variable number of) discount-s.
FXSL is a pure XSLT implementation of higher order functions. In this example the higher-order function f:map() is used to map the function f:product() on the list of children of every sale element. Then the results are summed to produce the final result.
All of Dimitre's solutions work and he's right that you don't need to use extension functions but sometimes it makes life easier. It's not too harmful, especially when you use exslt extensions which are supported across multiple XSLT processors. Also, the reason you're getting the sequence errors is probably because you're using an XSLT 1 processor.
If you want to persist with your chosen solution, you'll need to use Saxon or some other XSLT processor that supports XSLT 2.
Otherwise, here's an alternative method of doing it in XSLT 1. This will work in most XSLT processors and some peope might find it easier to grok than the recursive version. Personally, I prefer the recursive version (Dimitre's 3rd proposal) because it is more portable.
<xsl:stylesheet version="1.0"
xmlns:ex="http://exslt.org/common"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template name="GetProducts">
<xsl:param name="left"/>
<xsl:param name="right"/>
<xsl:for-each select="$left/text()">
<product>
<xsl:value-of select="number(.) * number($right[position()])"/>
</product>
</xsl:for-each>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="products">
<xsl:call-template name="GetProducts">
<xsl:with-param name="left" select="Parts/Part/Rate"/>
<xsl:with-param name="right" select="Parts/Part/Quantity"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="sum(ex:node-set($products)/product)"/>
</xsl:template>
</xsl:stylesheet>