Can any guide me to split the given xml element values into multiple child elements based on a token. Here is my sample input xml and desired output. I have a limitation to use xsl 1.0. Thank you.
Input XML:
<?xml version='1.0' encoding='UTF-8'?>
<SQLResults>
<SQLResult>
<ACTION1>Action1</ACTION1>
<ACTION2>Action2</ACTION2>
<Encrypt>Program=GPG;Code=23FCS;</Encrypt>
<SENDER>Program=WebPost;Protocol=WS;Path=/home/Inbound</SENDER>
</SQLResult>
</SQLResults>
Output XML:
<?xml version='1.0' encoding='UTF-8'?>
<SQLResults>
<SQLResult>
<ACTION1>Action1</ACTION1>
<ACTION2>Action2</ACTION2>
<Encrypt>
<Program>GPG</Program>
<Code>23FCS</Code>
</Encrypt>
<SENDER>
<Program>Action4</Program>
<Protocol>WS</Protocol>
<Path>/home/Inbound</Path>
</SENDER>
</SQLResult>
</SQLResults>
In XSLT 2 it would be easy, just with the following template:
<xsl:template match="Encrypt|SENDER">
<xsl:copy>
<xsl:analyze-string select="." regex="(\w+)=([\w/]+);?">
<xsl:matching-substring>
<element name="{regex-group(1)}">
<xsl:value-of select="regex-group(2)"/>
</element>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:copy>
</xsl:template>
Because you want to do it in XSLT 1, you have to express it another way.
Instead of analyze-string you have to:
Tokenize the content into non-empty tokens contained between ; chars.
You have to add tokenize template.
Each such token divide into 2 substrings, before and after = char.
Create an element with the name equal to the first substring.
Write the content of this element - the second substring.
XSLT 1 has also such limitation that the result of the tokenize template
is a result tree fragment (RTF) not the node set and thus it cannot be
used in XPath expressions.
To circumvent this limitation, you must use exsl:node-set function.
So the whole script looks like below:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="Encrypt|SENDER">
<xsl:copy>
<xsl:variable name="tokens">
<xsl:call-template name="tokenize">
<xsl:with-param name="txt" select="."/>
<xsl:with-param name="delim" select="';'"/>
</xsl:call-template>
</xsl:variable>
<xsl:for-each select="exsl:node-set($tokens)/token">
<xsl:variable name="t1" select="substring-before(., '=')"/>
<xsl:variable name="t2" select="substring-after(., '=')"/>
<xsl:element name="{$t1}">
<xsl:value-of select="$t2" />
</xsl:element>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="txt" />
<xsl:param name="delim" select="' '" />
<xsl:choose>
<xsl:when test="$delim and contains($txt, $delim)">
<token>
<xsl:value-of select="substring-before($txt, $delim)" />
</token>
<xsl:call-template name="tokenize">
<xsl:with-param name="txt" select="substring-after($txt, $delim)" />
<xsl:with-param name="delim" select="$delim" />
</xsl:call-template>
</xsl:when>
<xsl:when test="$txt">
<token><xsl:value-of select="$txt" /></token>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
</xsl:transform>
The below is my sgml file structure:
<em>
<pgblk revdate="20130901">
<task revdate='20140901'>
<p>random texts and few more inner tags</p>
</task>
<task revdate='20150901'>
<p>random texts and few more inner tags</p>
</task>
<task revdate='20160901'>
<p>random texts and few more inner tags</p>
</task>
</pgblk>
I have many tags with same name (task tag) and am trying to get the greatest revdate here. My XSLT is as follows:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:template match="/pgblk">
<xsl:variable name="updatedrevdate">
<xsl:for-each select="task">
<xsl:sort select="#revdate" data-type="number" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="#revdate" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
</xsl:template>
</xsl:stylesheet>
Now i am getting the greatest task tag value..but when i use the condition inside the same template to compare the greatest tag and the pageblock value
"Operations to PRINT THE GREATEST REVDATE"
The comparison is always getting the first revdate from task and comparing with pageblock revdate. It is not getting the greatest from the task.
Any help is much appreciated. Thanks.
Please check if the following XSLT code helps you make changes to your code. Need to change the template matching to
<xsl:template match="pgblk">
The solution does the comparison and outputs the dates.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:strip-space elements="*"/>
<xsl:template match="pgblk">
<xsl:variable name="updatedrevdate">
<xsl:for-each select="task">
<xsl:sort select="#revdate" data-type="number" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="#revdate" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- output the dates -->
<xsl:value-of select="concat('Page Block Date: ', #revdate)" />
<xsl:text>
</xsl:text>
<xsl:value-of select="concat('Max Task Date: ', $updatedrevdate)" />
<xsl:text>
</xsl:text>
<xsl:choose>
<xsl:when test="#revdate > $updatedrevdate">
<xsl:value-of select="concat('Max date after comparison: ', #revdate)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('Max date after comparison: ', $updatedrevdate)" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Output
Page Block Date: 20130901
Max Task Date: 20160901
Max date after comparison: 20160901
I need to convert the following content
<QTLS_ITEM>
<ID>123</ID>
<ID1>1345</ID1>
<SERAIL_NUMBER>102697704257</SERAIL_NUMBER>
<PROD_NAME>upgrade</PROD_NAME>
</QTLS_ITEM>
<QTLS_ITEM>
<ID>123</ID>
<ID1>1345</ID1>
<SERAIL_NUMBER>102697704257</SERAIL_NUMBER>
<PROD_NAME>Plugin</PROD_NAME>
</QTLS_ITEM>
<QTLS_ITEM>
<ID>123</ID>
<ID1>1345</ID1>
<SERAIL_NUMBER>102697704257</SERAIL_NUMBER>
<PROD_NAME>License</PROD_NAME>
</QTLS_ITEM>
This is a looping element type.<QTLS_ITEM> is a repeating element. For each element I have to concatenate those two fields and get the value as below.
I want to transform it into a single Element like
<Item_description>
102697704257 upgrade
102697704257 Plugin
102697704257 License
<Item_Description>
Which means I need to concatenate both the SERAIL_NUMBER and PROD_NAME.
Can anyone help on this?
<xsl:template name="string-join">
<xsl:param name="nodes"/>
<xsl:param name="delimiter"/>
<xsl:for-each select="$nodes">
<xsl:value-of select="."/>
<xsl:if test="$nodes[position()!=last()-1]">
<xsl:value-of select="$delimiter"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
I am using this to get the Serial numbers separated by comma. But I want to concatenate and get the result in the above format.
The answer for my case is
<xsl:template name="join">
<xsl:param name="list"/>
<xsl:param name="separator"/>
<xsl:for-each select="db:SERAIL_NUMBER | db:PROD_NAME">($list value)
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:value-of select="$separator"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
The input that you show us is not well-formed XML, because it does not have a single root element. Given a well-formed XML input such as:
XML
<root>
<QTLS_ITEM>
<SERAIL_NUMBER>102697704257</SERAIL_NUMBER>
<PROD_NAME>upgrade</PROD_NAME>
</QTLS_ITEM>
<QTLS_ITEM>
<SERAIL_NUMBER>102697704257</SERAIL_NUMBER>
<PROD_NAME>Plugin</PROD_NAME>
</QTLS_ITEM>
<QTLS_ITEM>
<SERAIL_NUMBER>102697704257</SERAIL_NUMBER>
<PROD_NAME>License</PROD_NAME>
</QTLS_ITEM>
</root>
you can do simply:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<Item_description>
<xsl:for-each select="QTLS_ITEM">
<xsl:value-of select="SERAIL_NUMBER, PROD_NAME" />
<xsl:text>
</xsl:text>
</xsl:for-each>
</Item_description>
</xsl:template>
</xsl:stylesheet>
to get:
Result
<?xml version="1.0" encoding="UTF-8"?>
<Item_description>102697704257 upgrade
102697704257 Plugin
102697704257 License
</Item_description>
This has worked out in my case
<xsl:template name="join">
<xsl:param name="list"/>
<xsl:param name="separator"/>
<xsl:for-each select="db:SERAIL_NUMBER|db:PROD_NAME">($list value)
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:value-of select="$separator"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
I have this xml file.
<?xml version="1.0" encoding="utf-8"?>
<racine>
<index>
<Parent nom="00000002" Name="" Address="" />
<Meter numSerie="00000002" />
<arrêté dateArrêté="28/02/2015 00:00:00">
<ValeurIndex Libelle="Val1">23.334</ValeurIndex>
<ValeurIndex Libelle="Val2">5.186</ValeurIndex>
<ValeurIndex Libelle="Val3">2.79</ValeurIndex>
</arrêté>
</index>
</racine>
and I would like to convert it in a txt file, where I need to add a new row with the value of max between Val1 and Val2, as the example below.
Val1,23.334
Val2,5.186
Val3,2.79
MaxVal1Vl2,23.334
What I created it was something like:
<xsl:choose>
<xsl:when test="#Libelle = 'Val1'">Val1</xsl:when>
<xsl:when test="#Libelle = 'Val2'">Val2</xsl:when>
<xsl:when test="#Libelle = 'Val3'">Val3</xsl:when>
</xsl:choose>
<xsl:text>,</xsl:text>
<xsl:value-of select="."/>
<xsl:text>
</xsl:text>
how can I add the 4 row being the max of Val1 and Val2?
Thanks
Try:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8"/>
<xsl:template match="/racine">
<xsl:for-each select="index/arrêté/ValeurIndex">
<xsl:value-of select="#Libelle"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="."/>
<xsl:text>
</xsl:text>
</xsl:for-each>
<xsl:variable name="v1" select="index/arrêté/ValeurIndex[#Libelle='Val1']" />
<xsl:variable name="v2" select="index/arrêté/ValeurIndex[#Libelle='Val2']" />
<xsl:text>MaxVal1Vl2,</xsl:text>
<xsl:choose>
<xsl:when test="$v1 > $v2">
<xsl:value-of select="$v1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$v2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I need to build up a string using XSLT and separate each string with a comma but not include a comma after the last string. In my example below I will have a trailing comma if I have Distribution node and not a Note node for instance. I don't know of anyway to build up a string as a variable and then truncate the last character in XSLT. Also this is using the Microsoft XSLT engine.
My String =
<xsl:if test="Locality != ''">
<xsl:value-of select="Locality"/>,
</xsl:if>
<xsl:if test="CollectorAndNumber != ''">
<xsl:value-of select="CollectorAndNumber"/>,
</xsl:if>
<xsl:if test="Institution != ''">
<xsl:value-of select="Institution"/>,
</xsl:if>
<xsl:if test="Distribution != ''">
<xsl:value-of select="Distribution"/>,
</xsl:if>
<xsl:if test="Note != ''">
<xsl:value-of select="Note"/>
</xsl:if>
[Man there's gotta be a better way to enter into this question text box :( ]
This is very easy to accomplish with XSLT (No need to capture the results in a variable, or to use special named templates):
I. 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:for-each select=
"Locality/text() | CollectorAndNumber/text()
| Institution/text() | Distribution/text()
| Note/text()
"
>
<xsl:value-of select="."/>
<xsl:if test="not(position() = last())">,</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<root>
<record>
<Locality>Locality</Locality>
<CollectorAndNumber>CollectorAndNumber</CollectorAndNumber>
<Institution>Institution</Institution>
<Distribution>Distribution</Distribution>
<Note></Note>
<OtherStuff>Unimportant</OtherStuff>
</record>
</root>
the wanted result is produced:
Locality,CollectorAndNumber,Institution,Distribution
If the wanted elements should be produced not in document order (something not required in the question, but raised by Tomalak), it is still quite easy and elegant to achieve this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:param name="porderedNames"
select="' CollectorAndNumber Locality Distribution Institution Note '"/>
<xsl:template match="/*/*">
<xsl:for-each select=
"*[contains($porderedNames, concat(' ',name(), ' '))]">
<xsl:sort data-type="number"
select="string-length(
substring-before($porderedNames,
concat(' ',name(), ' ')
)
)"/>
<xsl:value-of select="."/>
<xsl:if test="not(position() = last())">,</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Here the names of the wanted elements and their wanted order are provided in the string parameter $porderedNames, which contains a space-separated list of all wanted names.
When the above transformation is applied on the same XML document, the wanted result is produced:
CollectorAndNumber,Locality,Distribution,Institution
II. XSLT 2.0:
In XSLT this task is even simpler (again, no special function is necessary):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*/*">
<xsl:value-of separator="," select=
"(Locality, CollectorAndNumber,
Institution, Distribution,
Note)[text()]" />
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the same XML document, the same correct result is produced:
Locality,CollectorAndNumber,Institution,Distribution
Do note that the wanted elements will be produced in any desired order, because we are using the XPath 2.0 sequence type (vs the union in the XSLT 1.0 solution), which by definition contains items in any desired (specified) order.
I would prefer a short call-template to join the node values together. This also works if a node in the middle of your concatenated list, e.g. Institution, is missing:
<xsl:template name="join">
<xsl:param name="list" />
<xsl:param name="separator"/>
<xsl:for-each select="$list">
<xsl:value-of select="." />
<xsl:if test="position() != last()">
<xsl:value-of select="$separator" />
</xsl:if>
</xsl:for-each>
</xsl:template>
Here is a short example how to use it:
Sample input document:
<?xml version="1.0" encoding="utf-8"?>
<items>
<item>
<Locality>locality1</Locality>
<CollectorAndNumber>collectorAndNumber1</CollectorAndNumber>
<Distribution>distribution1</Distribution>
<Note>note1</Note>
</item>
<item>
<Locality>locality2</Locality>
<CollectorAndNumber>collectorAndNumber2</CollectorAndNumber>
<Institution>institution2</Institution>
<Distribution>distribution2</Distribution>
<Note>note2</Note>
</item>
<item>
<Locality>locality3</Locality>
<CollectorAndNumber>collectorAndNumber3</CollectorAndNumber>
<Institution>institution3</Institution>
<Distribution>distribution3</Distribution>
</item>
</items>
XSL transformation:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<summary>
<xsl:apply-templates />
</summary>
</xsl:template>
<xsl:template match="item">
<item>
<xsl:call-template name="join">
<xsl:with-param name="list" select="Locality | CollectorAndNumber | Institution | Distribution | Note" />
<xsl:with-param name="separator" select="','" />
</xsl:call-template>
</item>
</xsl:template>
<xsl:template name="join">
<xsl:param name="list" />
<xsl:param name="separator"/>
<xsl:for-each select="$list">
<xsl:value-of select="." />
<xsl:if test="position() != last()">
<xsl:value-of select="$separator" />
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Generated output document:
<?xml version="1.0" encoding="utf-8"?>
<summary>
<item>locality1,collectorAndNumber1,distribution1,note1</item>
<item>locality2,collectorAndNumber2,institution2,distribution2,note2</item>
<item>locality3,collectorAndNumber3,institution3,distribution3</item>
</summary>
NB: If you were using XSLT/XPath 2.0 then there would be fn:string-join
fn:string-join**($operand1 as string*, $operand2 as string*) as string
which could be used as follows:
fn:string-join({Locality, CollectorAndNumber, Distribution, Note}, ",")
Supposing you have something like the following input XML:
<root>
<record>
<Locality>Locality</Locality>
<CollectorAndNumber>CollectorAndNumber</CollectorAndNumber>
<Institution>Institution</Institution>
<Distribution>Distribution</Distribution>
<Note>Note</Note>
<OtherStuff>Unimportant</OtherStuff>
</record>
</root>
Then this template would do it:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="text" />
<xsl:template match="record">
<xsl:variable name="values">
<xsl:apply-templates mode="concat" select="Locality" />
<xsl:apply-templates mode="concat" select="CollectorAndNumber" />
<xsl:apply-templates mode="concat" select="Institution" />
<xsl:apply-templates mode="concat" select="Distribution" />
<xsl:apply-templates mode="concat" select="Note" />
</xsl:variable>
<xsl:value-of select="substring($values, 1, string-length($values) - 1)" />
<xsl:value-of select="'
'" /><!-- LF -->
</xsl:template>
<xsl:template match="Locality | CollectorAndNumber | Institution | Distribution | Note" mode="concat">
<xsl:value-of select="." />
<xsl:text>,</xsl:text>
</xsl:template>
</xsl:stylesheet>
Output on my system:
Locality,CollectorAndNumber,Institution,Distribution,Note
I think it might be useful to mention,
position() doesn't work right when I use a complicated select
that filters some nodes,
in that case I came up which this trick:
you can define a string variable that hold value of nodes, separated
by a specific character, then by using str:tokenize()
you can create a complete node list which position works fine with it.
something like this:
<!-- Since position() doesn't work as expected(returning node position of current
node list), I got round it by a string variable and tokenizing it in which
absolute position is equal to relative(context) position. -->
<xsl:variable name="measObjLdns" >
<xsl:for-each select="h:measValue[#measObjLdn=$currentMeasObjLdn]/h:measResults" >
<xsl:value-of select="concat(.,'---')"/> <!-- is an optional separator. -->
</xsl:for-each>
</xsl:variable>
<xsl:for-each select="str:tokenize($measObjLdns,'---')" ><!-- Since position() doesn't
work as expected(returning node position of current node list),
I got round it by a string variable and tokenizing it in which
absolute position is equal to relative(context) position. -->
<xsl:value-of select="."></xsl:value-of>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
Do you not have a value that is always going to be there? If you do then you can turn it around and put commas infront of everything apart from the first item (which would be your value that's always there).
This would be a bit messy but might do the trick if there's only a few elements like in your example:
<xsl:if test="Locality != ''">
<xsl:value-of select="Locality"/>
<xsl:if test="CollectorAndNumber != '' or Institution != '' or Distribution != '' or Note != ''">
<xsl:value-of select="','"/>
</xsl:if>
</xsl:if>
<xsl:if test="CollectorAndNumber != ''">
<xsl:value-of select="CollectorAndNumber"/>
<xsl:if test="Institution != '' or Distribution != '' or Note != ''">
<xsl:value-of select="','"/>
</xsl:if>
</xsl:if>
<xsl:if test="Institution != ''">
<xsl:value-of select="Institution"/>
<xsl:if test="Distribution != '' or Note != ''">
<xsl:value-of select="','"/>
</xsl:if>
</xsl:if>
<xsl:if test="Distribution != ''">
<xsl:value-of select="Distribution"/>
<xsl:if test="Note != ''">
<xsl:value-of select="','"/>
</xsl:if>
</xsl:if>
<xsl:if test="Note != ''">
<xsl:value-of select="Note"/>
</xsl:if>