extracting sql data from xml - stored-procedures

Let me know your suggestions to extract SQL data from xml. The sample xml would be as below given as input to a procedure as a CLOB:
<filter>
<and>
<or>
<equals field="MARKET_NAME" value="Chicago"/>
<equals field="MARKET_NAME" value="BOSTON"/>
</or>
<or>
<equals field="RANK" value="1"/>
<equals field="RANK" value="2"/>
</or>
<between field="current_data" arg1="start_date" arg2="End_date"/>
<gt field="CUME" value="20"/>
<like field="DMA_NAME" value="%ABC%"/>
</and>
</filter>
The extracted data should look something like this :
(MARKET_NAME = 'Chicago' or MARKET_NAME = 'BOSTON') and
(RANK = 1 or RANK = 2) and
(current_date between start_date and End_Date) and
CUME > 20 and
DMA_NAME like '%ABC%'
Let me know your valuable tips..
Thanks in advance

one approach would be a XSL stylesheet.
but I would add to your XML, datatype. e.g.
<filter>
<and>
<or>
<equals field="MARKET_NAME" value="Chicago" datatype="string"/>
<equals field="MARKET_NAME" value="BOSTON" datatype="string"/>
</or>
<or>
<equals field="RANK" value="1" datatype="number"/>
<equals field="RANK" value="2" datatype="number"/>
</or>
<between field="current_data" arg1="start_date" arg2="End_date" datatype="field"/>
<between field="foo" arg1="20121230" arg2="20130101 01:12:23" datatype="date"/>
<gt field="CUME" value="20" datatype="number"/>
<like field="DMA_NAME" value="%ABC%" datatype="string"/>
</and>
</filter>
then an XSL (this is just a sample. you WILL need to improve this as its just a basic starter for you!)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
exclude-result-prefixes="xd"
version="1.0">
<xsl:output method="text"/>
<xsl:template name="string-replace-all">
<xsl:param name="text"/>
<xsl:param name="replace"/>
<xsl:param name="by"/>
<xsl:choose>
<xsl:when test="contains($text,$replace)">
<xsl:value-of select="substring-before($text,$replace)"/>
<xsl:value-of select="$by"/>
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="substring-after($text,$replace)"/>
<xsl:with-param name="replace" select="$replace"/>
<xsl:with-param name="by" select="$by"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="processDatatype">
<xsl:param name="value" />
<xsl:param name="datatype" />
<xsl:choose>
<xsl:when test="$datatype = 'string'">
<xsl:text>&apos;</xsl:text>
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="$value"/>
<xsl:with-param name="replace" select='"&apos;"'/>
<xsl:with-param name="by" select='"&apos;&apos;"'/>
</xsl:call-template>
<xsl:text>&apos;</xsl:text>
</xsl:when>
<xsl:when test="$datatype = 'date'">
<xsl:text>to_date(&apos;</xsl:text>
<xsl:value-of select="$value"/>
<xsl:text>&apos;,&apos;yyyymmdd hh24:mi:ss&apos;)</xsl:text>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$value"/></xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="gt">
<xsl:value-of select="#field"/>
<xsl:text> > </xsl:text>
<xsl:call-template name="processDatatype">
<xsl:with-param name="value"><xsl:value-of select="#value"/></xsl:with-param>
<xsl:with-param name="datatype"><xsl:value-of select="#datatype"/></xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="lt">
<xsl:value-of select="#field"/>
<xsl:text> < </xsl:text>
<xsl:call-template name="processDatatype">
<xsl:with-param name="value"><xsl:value-of select="#value"/></xsl:with-param>
<xsl:with-param name="datatype"><xsl:value-of select="#datatype"/></xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="between">
<xsl:value-of select="#field"/>
<xsl:text> between </xsl:text>
<xsl:call-template name="processDatatype">
<xsl:with-param name="value"><xsl:value-of select="#arg1"/></xsl:with-param>
<xsl:with-param name="datatype"><xsl:value-of select="#datatype"/></xsl:with-param>
</xsl:call-template>
<xsl:text> and </xsl:text>
<xsl:call-template name="processDatatype">
<xsl:with-param name="value"><xsl:value-of select="#arg2"/></xsl:with-param>
<xsl:with-param name="datatype"><xsl:value-of select="#datatype"/></xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="like">
<xsl:value-of select="#field"/>
<xsl:text> like </xsl:text>
<xsl:call-template name="processDatatype">
<xsl:with-param name="value"><xsl:value-of select="#value"/></xsl:with-param>
<xsl:with-param name="datatype"><xsl:value-of select="#datatype"/></xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="equals">
<xsl:value-of select="#field"/>
<xsl:text> = </xsl:text>
<xsl:call-template name="processDatatype">
<xsl:with-param name="value"><xsl:value-of select="#value"/></xsl:with-param>
<xsl:with-param name="datatype"><xsl:value-of select="#datatype"/></xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="and">
<xsl:for-each select="*">
<xsl:if test="position() != 1">
<xsl:text>and </xsl:text>
</xsl:if>
<xsl:choose>
<xsl:when test="name() = 'like'">
<xsl:text>(</xsl:text>
<xsl:call-template name="like" />
<xsl:text>)
</xsl:text>
</xsl:when>
<xsl:when test="name() = 'gt'">
<xsl:text>(</xsl:text>
<xsl:call-template name="gt" />
<xsl:text>)
</xsl:text>
</xsl:when>
<xsl:when test="name() = 'lt'">
<xsl:text>(</xsl:text>
<xsl:call-template name="lt" />
<xsl:text>)
</xsl:text>
</xsl:when>
<xsl:when test="name() = 'equals'">
<xsl:text>(</xsl:text>
<xsl:call-template name="equals" />
<xsl:text>)
</xsl:text>
</xsl:when>
<xsl:when test="name() = 'between'">
<xsl:text>(</xsl:text>
<xsl:call-template name="between" />
<xsl:text>)
</xsl:text>
</xsl:when>
<xsl:when test="name() = 'or'">
<xsl:text>(</xsl:text>
<xsl:call-template name="or" />
<xsl:text>)
</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<xsl:template name="or">
<xsl:for-each select="*">
<xsl:if test="position() != 1">
<xsl:text>or </xsl:text>
</xsl:if>
<xsl:choose>
<xsl:when test="name() = 'like'">
<xsl:text>(</xsl:text>
<xsl:call-template name="like" />
<xsl:text>)
</xsl:text>
</xsl:when>
<xsl:when test="name() = 'gt'">
<xsl:text>(</xsl:text>
<xsl:call-template name="gt" />
<xsl:text>)
</xsl:text>
</xsl:when>
<xsl:when test="name() = 'lt'">
<xsl:text>(</xsl:text>
<xsl:call-template name="lt" />
<xsl:text>)
</xsl:text>
</xsl:when>
<xsl:when test="name() = 'equals'">
<xsl:text>(</xsl:text>
<xsl:call-template name="equals" />
<xsl:text>)
</xsl:text>
</xsl:when>
<xsl:when test="name() = 'between'">
<xsl:text>(</xsl:text>
<xsl:call-template name="between" />
<xsl:text>)
</xsl:text>
</xsl:when>
<xsl:when test="name() = 'and'">
<xsl:text>(</xsl:text>
<xsl:call-template name="and" />
<xsl:text>)
</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<xsl:template match="/filter">
<xsl:text>where </xsl:text>
<xsl:for-each select="*">
<xsl:choose>
<xsl:when test="name() = 'and'">
<xsl:call-template name="and" />
</xsl:when>
<xsl:when test="name() = 'or'">
<xsl:call-template name="or" />
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
example:
out XSL stored as an XMLTYPE:
SQL> select * from xsl;
NAME
--------------------
XSL
--------------------------------------------------------------------------------
xml_to_sql
<?xml version="1.0" encoding="WINDOWS-1252"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xd="http:
//www.oxygenxml.com/ns/doc/xsl" exclude-result-prefixes="xd" version="1.0">
<xsl:output method="text"/>
<xsl:template name="string-replace-all">
<xsl:param name="text"/>
...
now we use the transform function of XMLTYPE on the input XML to get the result:
SQL> select dbms_xmlgen.convert(xmltype('<?xml-stylesheet type="text/xsl" href="test.xsl"?>
2 <filter>
3 <and>
4 <or>
5 <equals field="MARKET_NAME" value="Chicago" datatype="string"/>
6 <equals field="MARKET_NAME" value="BOSTON" datatype="string"/>
7 </or>
8 <or>
9 <equals field="RANK" value="1" datatype="number"/>
10 <equals field="RANK" value="2" datatype="number"/>
11 </or>
12 <between field="current_data" arg1="start_date" arg2="End_date" datatype="field"/>
13 <between field="foo" arg1="20121230" arg2="20130101 01:12:23" datatype="date"/>
14 <gt field="CUME" value="20" datatype="number"/>
15 <like field="DMA_NAME" value="%AB&apos;C%" datatype="string"/>
16 </and>
17 </filter>').transform(xsl.xsl).getclobval(), 1)
18 from xsl
19 /
DBMS_XMLGEN.CONVERT(XMLTYPE('<
--------------------------------------------------------------------------------
where ((MARKET_NAME = 'Chicago')
or (MARKET_NAME = 'BOSTON')
)
and ((RANK = 1)
or (RANK = 2)
)
and (current_data between start_date and End_date)
and (foo between to_date('20121230','yyyymmdd hh24:mi:ss') and to_date('20130101
01:12:23','yyyymmdd hh24:mi:ss'))
and (CUME > 20)
and (DMA_NAME like '%AB''C%')

Related

Find and replace for the particular texts within same text() node, more than once

Please suggest to find replace the required text within one text() node, some times find-replace text repeated more than once.
Here, scripts should exclue the element '<p>'. Working fine when 'find' text occuring only once in a text node, if it occures more than once, then skipping the replace process (see first <p1>). Please suggest.
XML:
<article>
<p1>The text 1111, 2222, 3333 and 4444 are some values, 1111 another occurance.</p1>
<p1>The text 1111, 2222, 3333 and 4444 are some values</p1>
<p>The text 1111, 2222, 3333 and 4444 are some values</p>
<p1>The text aaaaa, bbbbb, ccccc and ddddd are another set of values</p1>
<p1>The text <i>aaaaa</i>, <b>bbbbb</b> and <c>ccccc</c> are in different form</p1>
</article>
XSLT 2.0:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#* | node()"/></xsl:copy>
</xsl:template>
<xsl:template match="*/text()">
<xsl:call-template name="tempFindReplace">
<xsl:with-param name="pText1" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="tempFindReplace">
<xsl:param name="pText1"/>
<xsl:if test="string-length($pText1) > 0">
<xsl:choose>
<xsl:when test="ancestor::p1">
<xsl:choose>
<xsl:when test="contains($pText1, '1111')">
<xsl:value-of select="substring-before($pText1,'1111')"/><xsl:text>Alpha1</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, '1111')"/></xsl:call-template></xsl:when>
<xsl:when test="contains($pText1, '2222')">
<xsl:value-of select="substring-before($pText1,'2222')"/><xsl:text>Alpha2</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, '2222')"/></xsl:call-template></xsl:when>
<xsl:when test="contains($pText1, '3333')">
<xsl:value-of select="substring-before($pText1,'3333')"/><xsl:text>Alpha3</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, '3333')"/></xsl:call-template></xsl:when>
<xsl:when test="contains($pText1, '4444')">
<xsl:value-of select="substring-before($pText1,'4444')"/><xsl:text>Alpha4</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, '4444')"/></xsl:call-template></xsl:when>
<xsl:when test="contains($pText1, 'aaaaa')">
<xsl:value-of select="substring-before($pText1,'aaaaa')"/><xsl:text>Beta1</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, 'aaaaa')"/></xsl:call-template></xsl:when>
<xsl:when test="contains($pText1, 'bbbbb')">
<xsl:value-of select="substring-before($pText1,'bbbbb')"/><xsl:text>Beta2</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, 'bbbbb')"/></xsl:call-template></xsl:when>
<xsl:when test="contains($pText1, 'ccccc')">
<xsl:value-of select="substring-before($pText1,'ccccc')"/><xsl:text>Beta3</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, 'ccccc')"/></xsl:call-template></xsl:when>
<xsl:when test="contains($pText1, 'ddddd')">
<xsl:value-of select="substring-before($pText1,'ddddd')"/><xsl:text>Beta4</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, 'ddddd')"/></xsl:call-template></xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($pText1,1,1)"/>
<xsl:call-template name="tempFindReplace">
<xsl:with-param name="pText1" select="substring($pText1, 2)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Required Result:
<article>
<p1>The text Alpha1, Alpha2, Alpha3 and Alpha4 are some values, Alpha1 another occurance.</p1><!-- Alpha1 only replacing, but other texts also required change, I assume here failing because of 1111 appearing twice-->
<p1>The text Alpha1, Alpha2, Alpha3 and Alpha4 are some values</p1>
<p>The text 1111, 2222, 3333 and 4444 are some values</p>
<p1>The text Beta1, Beta2, Beta3 and Beta4 are another set of values</p1>
<p1>The text <i>Beta1</i>, <b>Beta2</b> and <c>Beta3</c> are in different form</p1>
</article>
I continue to think you can use the replace function repeatedly, for instance in XSLT 3 with xsl:iterate:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="replacements" as="array(xs:string)*"
select="['1111', 'Alpha1'], ['2222', 'Alpha2'], ['3333', 'Alpha3'], ['4444', 'Alpha4'],
['aaaaa', 'Beta1'], ['bbbbb', 'Beta2'], ['ccccc', 'Beta3'], ['ddddd', 'Beta4'] "/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="article/*[not(self::p)]//text()">
<xsl:iterate select="$replacements">
<xsl:param name="text" select="."/>
<xsl:on-completion>
<xsl:value-of select="$text"/>
</xsl:on-completion>
<xsl:next-iteration>
<xsl:with-param name="text" select="replace($text, .(1), .(2))"/>
</xsl:next-iteration>
</xsl:iterate>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bwdwrL
XSLT 2:
called template twice, and got the result.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#* | node()"/></xsl:copy>
</xsl:template>
<xsl:template match="*[ancestor-or-self::*[matches(name(), '^(p1|p2|p3)$')]]/text()">
<xsl:variable name="var1">
<xsl:call-template name="tempFindReplace">
<xsl:with-param name="pText1" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="tempFindReplace">
<xsl:with-param name="pText1" select="$var1"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="tempFindReplace">
<xsl:param name="pText1"/>
<xsl:if test="string-length($pText1) > 0">
<xsl:choose>
<xsl:when test="contains($pText1, '1111')">
<xsl:value-of select="substring-before($pText1,'1111')"/><xsl:text>Alpha1</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, '1111')"/></xsl:call-template></xsl:when>
<xsl:when test="contains($pText1, '2222')">
<xsl:value-of select="substring-before($pText1,'2222')"/><xsl:text>Alpha2</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, '2222')"/></xsl:call-template></xsl:when>
<xsl:when test="contains($pText1, '3333')">
<xsl:value-of select="substring-before($pText1,'3333')"/><xsl:text>Alpha3</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, '3333')"/></xsl:call-template></xsl:when>
<xsl:when test="contains($pText1, '4444')">
<xsl:value-of select="substring-before($pText1,'4444')"/><xsl:text>Alpha4</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, '4444')"/></xsl:call-template></xsl:when>
<xsl:when test="contains($pText1, 'aaaaa')">
<xsl:value-of select="substring-before($pText1,'aaaaa')"/><xsl:text>Beta1</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, 'aaaaa')"/></xsl:call-template></xsl:when>
<xsl:when test="contains($pText1, 'bbbbb')">
<xsl:value-of select="substring-before($pText1,'bbbbb')"/><xsl:text>Beta2</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, 'bbbbb')"/></xsl:call-template></xsl:when>
<xsl:when test="contains($pText1, 'ccccc')">
<xsl:value-of select="substring-before($pText1,'ccccc')"/><xsl:text>Beta3</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, 'ccccc')"/></xsl:call-template></xsl:when>
<xsl:when test="contains($pText1, 'ddddd')">
<xsl:value-of select="substring-before($pText1,'ddddd')"/><xsl:text>Beta4</xsl:text>
<xsl:call-template name="tempFindReplace"><xsl:with-param name="pText1" select="substring-after($pText1, 'ddddd')"/></xsl:call-template></xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($pText1,1,1)"/>
<xsl:call-template name="tempFindReplace">
<xsl:with-param name="pText1" select="substring($pText1, 2)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Subtraction by decimal number

I have a to subtract the amount -1 based on the condition.Please any one help.
Input:
<JD>
<GP xmlns="">
I xmlns="">
<PK>40</PK>
<A/>
<AMNT>11659650.15</AMNT>
<B/>
<C/>
</I>
<I xmlns="">
<PK>50</PK>
<A/>
<AMNT>11659650.15</AMNT>
<B/>
<C/>
</I>
</GP>
</JD>
Tried with below XSLT and got 1.165964915E7 for 50.
<xsl:for-each select="JD/mo:GP/I">
<xsl:if test="PK='40'">
<xsl:variable name="a" select="AMNT"/>
<xsl:element name="AMT">
<xsl:value-of select="$a"/>
</xsl:element>
</xsl:if>
<xsl:if test="PK='50'">
<xsl:variable name="a" select="AMNT"/>
<xsl:element name="AMT">
<xsl:value-of select="$a - 1"/>
</xsl:element>
</xsl:if>
Considering your given input as following:
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<JD>
<GP>
<I>
<PK>40</PK>
<A />
<AMNT>11659650.15</AMNT>
<B />
<C />
</I>
<I>
<PK>50</PK>
<A />
<AMNT>11659650.15</AMNT>
<B />
<C />
</I>
</GP>
</JD>
</Root>
In XSLT 2.0, you can try it using xs:decimal as below:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/Root">
<xsl:for-each select="JD/mo:GP/I">
<xsl:if test="PK='40'">
<xsl:variable name="a" select="AMNT" />
<xsl:element name="AMT">
<xsl:value-of select="$a" />
</xsl:element>
</xsl:if>
<xsl:if test="PK='50'">
<xsl:variable name="a" select="AMNT" />
<xsl:element name="AMT">
<xsl:value-of select="xs:decimal($a) - 1" />
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:template>
In XSLT 1.0, Use the format-number() function:
<xsl:template match="/Root">
<xsl:for-each select="JD/mo:GP/I">
<xsl:if test="PK='40'">
<xsl:variable name="a" select="AMNT" />
<xsl:element name="AMT">
<xsl:value-of select="$a" />
</xsl:element>
</xsl:if>
<xsl:if test="PK='50'">
<xsl:variable name="a" select="AMNT" />
<xsl:element name="AMT">
<xsl:value-of select="format-number($a - 1, '0.##')" />
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:template>

Aggregate nodes based on conditon under new parent

I have an given XML and I want to convert it in new xml and want to aggregate nodes based on status and orderId.
<?xml version="1.0" encoding="utf-8"?>
<OrderStatusUpdate>
<OrderStatusEvents>
<OrderStatusEvent>
<StoreCode>store1</StoreCode>
<OrderId>Order1</OrderId>
<ItemId>Item1</ItemId>
<OrderStatusDetails>
<OrderStatusDetail>
<OrderStatusEventTimeStamp>2017-03-19 03:37:05</OrderStatusEventTimeStamp>
<StatusName>Cancelled</StatusName>
</OrderStatusDetail>
</OrderStatusDetails>
</OrderStatusEvent>
<OrderStatusEvent>
<StoreCode>Store1</StoreCode>
<OrderId>Order1</OrderId>
<ItemId>Item2</ItemId>
<OrderStatusDetails>
<OrderStatusDetail>
<OrderStatusEventTimeStamp>2017-03-19 03:48:35</OrderStatusEventTimeStamp>
<StatusName>Cancelled</StatusName>
</OrderStatusDetail>
</OrderStatusDetails>
</OrderStatusEvent>
<OrderStatusEvent>
<StoreCode>Store1</StoreCode>
<OrderId>Order1</OrderId>
<ItemId>Item3</ItemId>
<OrderStatusDetails>
<OrderStatusDetail>
<OrderStatusEventTimeStamp>2017-03-19 03:48:35</OrderStatusEventTimeStamp>
<StatusName>Shipped</StatusName>
</OrderStatusDetail>
</OrderStatusDetails>
</OrderStatusEvent>
<OrderStatusEvent>
<StoreCode>Store1</StoreCode>
<OrderId>Order2</OrderId>
<ItemId>Item1</ItemId>
<OrderStatusDetails>
<OrderStatusDetail>
<OrderStatusEventTimeStamp>2017-03-19 03:48:35</OrderStatusEventTimeStamp>
<StatusName>Cancelled</StatusName>
</OrderStatusDetail>
</OrderStatusDetails>
</OrderStatusEvent>
</OrderStatusEvents>
</OrderStatusUpdate>
And I want an output like this. Here I am grouping elements based on status and orderId.
<Orders>
<group name="CANCELLED">
<STATUS ID="CANCELLED" DESCRIPTION="Goods Cancelled">
<ORDER ID="Order1">
<ORDER_ITEM item="item1" />
<ORDER_ITEM item="item2" />
</ORDER>
</STATUS>
<STATUS ID="CANCELLED" DESCRIPTION="Goods Cancelled">
<ORDER ID="Order2">
<ORDER_ITEM item="item1" />
</ORDER>
</STATUS>
</group>
<group name="SHIPPED">
<STATUS ID="SHIPPED" DESCRIPTION="Goods SHIPPED">
<ORDER ID="Order1">
<ORDER_ITEM item="item3" />
</ORDER>
</STATUS>
<group>
</Orders>
I am using the following xslt and it is working fine. Is there any way to improve this.
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:template match="OrderStatusUpdate/OrderStatusEvents">
<Orders>
<xsl:for-each-group select="OrderStatusEvent" group-by="OrderStatusDetails/OrderStatusDetail/StatusName">
<xsl:variable name="group-name" select="current-grouping-key()" />
<group name="{current-grouping-key()}">
<xsl:for-each-group select="current-group()" group-by="OrderId">
<xsl:variable name="order-id" select="current-grouping-key()" />
<xsl:element name="STATUS">
<xsl:attribute name="ID"><xsl:value-of select="$group-name" /></xsl:attribute>
<xsl:attribute name="DESCRIPTION">Goods <xsl:value-of select="$group-name" /></xsl:attribute>
<xsl:element name="ORDER">
<xsl:attribute name="ID"><xsl:value-of select="$order-id" /></xsl:attribute>
<xsl:for-each select="current-group()">
<xsl:if test="$group-name = 'Shipped'">
<xsl:call-template name="Shipped">
<xsl:with-param name="nodes">
<xsl:copy-of select="." />
</xsl:with-param>
</xsl:call-template>
</xsl:if>
<xsl:if test="$group-name = 'Cancelled'">
<xsl:call-template name="Cancelled">
<xsl:with-param name="nodes">
<xsl:copy-of select="." />
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</xsl:element>
</xsl:element>
</xsl:for-each-group>
</group>
</xsl:for-each-group>
</Orders>
</xsl:template>
<xsl:template name="Shipped">
<xsl:param name="nodes">
</xsl:param>
<xsl:element name="ORDER_ITEM">
<xsl:attribute name="ID"><xsl:value-of select="$nodes/OrderStatusEvent/ItemId" /></xsl:attribute>
</xsl:element>
</xsl:template>
<xsl:template name="Cancelled">
<xsl:param name="nodes"></xsl:param>
<xsl:element name="ORDER_ITEM">
<xsl:attribute name="ID"><xsl:value-of select="$nodes/OrderStatusEvent/ItemId" /></xsl:attribute>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
It is easier to use literal result elements and attribute value templates as long as you don't need to compute element or attribute names at run-time and I don't think you need the two templates and call-template, it suffices to use
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:template match="OrderStatusUpdate/OrderStatusEvents">
<Orders>
<xsl:for-each-group select="OrderStatusEvent" group-by="OrderStatusDetails/OrderStatusDetail/StatusName">
<xsl:variable name="group-name" select="current-grouping-key()" />
<group name="{current-grouping-key()}">
<xsl:for-each-group select="current-group()" group-by="OrderId">
<xsl:variable name="order-id" select="current-grouping-key()" />
<STATUS ID="{$group-name}" DESCRIPTION="Goods {$group-name}">
<ORDER ID="{$order-id}">
<xsl:apply-templates select="current-group()"/>
</ORDER>
</STATUS>
</xsl:for-each-group>
</group>
</xsl:for-each-group>
</Orders>
</xsl:template>
<xsl:template match="OrderStatusEvent">
<ORDER_ITEM ID="{ItemId}"/>
</xsl:template>
</xsl:stylesheet>

Xslt for splitting a url in parts and combing them again

I want to build a sort of breadcrumb. I have link for example
http://server/site1/site2/site3
and want to build something like
http://serverhttp://server/site1...
How can I do this with xslt?
This can be accomplished with a recursive template:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="urlSample" select="'http://server/site1/site2/site3'" />
<xsl:template match="/">
<xsl:call-template name="UrlLinks">
<xsl:with-param name="url" select="$urlSample" />
</xsl:call-template>
</xsl:template>
<xsl:template name="UrlLinks">
<xsl:param name="url" />
<xsl:call-template name="UrlLinksIter">
<xsl:with-param name="portionSoFar" select="concat(substring-before($url, '://'), '://')" />
<xsl:with-param name="remainder" select="concat(substring-after($url, '://'), '/')" />
</xsl:call-template>
</xsl:template>
<xsl:template name="UrlLinksIter">
<xsl:param name="portionSoFar" />
<xsl:param name="remainder" />
<xsl:variable name="nextPart" select="substring-before($remainder, '/')" />
<xsl:variable name="nextRemainder" select="substring-after($remainder, '/')" />
<xsl:if test="normalize-space($nextRemainder) or normalize-space($nextPart)">
<xsl:variable name="url" select="concat($portionSoFar, $nextPart)"/>
<xsl:if test="normalize-space($nextPart)">
<!-- $nextPart could be empty if there are multiple slashes in a row-->
<a href="{$url}">
<xsl:value-of select="$url"/>
</a>
</xsl:if>
<xsl:call-template name="UrlLinksIter">
<xsl:with-param name="portionSoFar" select="concat($url, '/')" />
<xsl:with-param name="remainder" select="$nextRemainder" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When this is run on any input (since the sample value is in a variable here) this produces:
http://server
http://server/site1
http://server/site1/site2
http://server/site1/site2/site3

XSLT Wrapping text with multiple tags

I have this span element with a class of autbib-pc-bold-italic
<span class="autbib-pc-bold-italic">autbib</span>
I want to create element tags base on the #class attribute value:
My output should be:
<autbib><pc><bold><italic>autbib</italic></bold></pc></autbib>
Here is my xsl templates:
<xsl:template match="span[contains(#class,'autbib')]">
<xsl:call-template name="pbib.loop">
<xsl:with-param name="count" select="count(tokenize(#class, '-'))"/>
<xsl:with-param name="class" select="tokenize(#class, '-')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="pbib.loop">
<xsl:param name="index" select="1" />
<xsl:param name="count" select="count(tokenize(#class, '-')) + 1"/>
<xsl:param name="class" select="tokenize(#class, '-')"/>
<xsl:element name="{$class[1]}">
<xsl:if test="not($index = $count)">
<xsl:element name="{$class[$index]}">
<xsl:apply-templates/>
</xsl:element>
</xsl:if>
</xsl:element>
<xsl:if test="not($index = $count)">
<xsl:call-template name="pbib.loop">
<xsl:with-param name="index" select="$index + 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
And have this output which is wrong:
<autbib>
<pc>autbib</pc>
<bold>autbib</bold>
<italic>autbib</italic>
</autbib>
I need to have this output:
<autbib>
<pc>
<bold>
<italic>autbib</italic>
</bold>
</pc>
</autbib>
My problem is that I'm not sure where I should place xsl:apply-template so that tags wrap with each other.
Here is my suggestion:
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output indent="yes"/>
<xsl:template match="span[contains(#class,'autbib')]">
<xsl:param name="classes" select="tokenize(#class, '-')"/>
<xsl:choose>
<xsl:when test="not($classes[1])">
<xsl:apply-templates/>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{$classes[1]}">
<xsl:apply-templates select=".">
<xsl:with-param name="classes" select="$classes[position() gt 1]"/>
</xsl:apply-templates>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
With Saxon 9.4 that transforms the input
<span class="autbib-pc-bold-italic">autbib</span>
into the result
<autbib>
<pc>
<bold>
<italic>autbib</italic>
</bold>
</pc>
</autbib>

Resources