XSLT - Can't get data from node, when using xml:when - xslt

I have a problem getting data from a node, when I'm using xml:choose and xml:when. I only get the result NaN or the value from the main xml-file, even if.
Part of the XML-file:
<?xml version="1.0" encoding="UTF-8"?>
<Job xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
<Invoice>
<InvoiceLine>
<LineNo>1</LineNo>
<QtyInSecondUnit>56</QtyInSecondUnit>
<Quantity>56</Quantity>
<CustTaric>
<StatNo>34011100</StatNo>
<IssuingCountry>GB</IssuingCountry>
</CustTaric>
</InvoiceLine>
<InvoiceLine>
<LineNo>2</LineNo>
<QtyInSecondUnit>22</QtyInSecondUnit>
<Quantity>0</Quantity>
<CustTaric>
<StatNo>44152020</StatNo>
<IssuingCountry>GB</IssuingCountry>
</CustTaric>
</InvoiceLine>
</Invoice>
</Job>
Part of the XSLT-file:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="Job/Invoice"/>
</xsl:template>
<xsl:template match="Job/Invoice/InvoiceLine">
<xsl:apply-templates select="QtyInSecondUnit"/>
<xsl:apply-templates select="Quantity"/>
<xsl:apply-templates select="CustTaric/StatNo"/>
</xsl:template>
<xsl:template match="QtyInSecondUnit">
<xsl:choose>
<xsl:when test="/Job/Invoice/InvoiceLine/CustTaric/StatNo = '44152020'">
<xsl:value-of select="number(translate(Job/Invoice/InvoiceLine/NetMass,',','.')) div 25"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="Quantity">
<xsl:choose>
<xsl:when test="/Job/Invoice/InvoiceLine/CustTaric/StatNo = '44152020'">
<xsl:value-of select="'0'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="CustTaric/StatNo">
<xsl:value-of select="."/>
</xsl:template>
Hope there is somebody how can tell me (the noob) what I'm doing wrong here?

Here's the line producing NaN
<xsl:value-of select="number(translate(Job/Invoice/InvoiceLine/NetMass,',','.')) div 25"/>
There are two problems (one of which is probably where you have over-simplified your XML)
The xpath expression you are using will be relative to the current node you are positioned on. There is no Job element under the current QtyInSecondUnit element
There is no NetMass element in your XML in your question
Assuming NetMass does exist in your actual XML, and is a child of the parent InvoiceLine the expression you want is this
<xsl:value-of select="number(translate(../NetMass,',','.')) div 25"/>
There is also an issue with your xsl:when (possibly)
<xsl:when test="/Job/Invoice/InvoiceLine/CustTaric/StatNo = '44152020'">
This will test for any CustTaric/StatNo anywhere in the document. Perhaps you only want to test for the one in the current InvoiceLine? If so, do this...
<xsl:when test="../CustTaric/StatNo = '44152020'">
Note, you could rewrite your XSLT to put the logic in template matches, rather than xsl:choose
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Job/Invoice/InvoiceLine">
<xsl:apply-templates select="QtyInSecondUnit"/>
<xsl:apply-templates select="Quantity"/>
<xsl:apply-templates select="CustTaric/StatNo"/>
</xsl:template>
<xsl:template match="InvoiceLine[CustTaric/StatNo = '44152020']/QtyInSecondUnit">
<xsl:value-of select="number(translate(../NetMass,',','.')) div 25"/>
</xsl:template>
<xsl:template match="QtyInSecondUnit">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="InvoiceLine[CustTaric/StatNo = '44152020']/Quantity">
<xsl:value-of select="'0'"/>
</xsl:template>
<xsl:template match="Quantity">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="CustTaric/StatNo">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
Strictly speaking, the templates that just do <xsl:value-of select="."/> can be removed, as XSLT's built-in templates will do exactly the same thing if there is no matching template in the XSLT.

Related

Looping Element to Single Element in XSLT

I need to convert the following content
<QTLS_ITEM>
<ID>123</ID>
<ID1>1345</ID1>
<SERAIL_NUMBER>1026977­04257</SERAIL_NUMBER>
<PROD_NAME>upgrade</PROD_NAME>
</QTLS_ITEM>
<QTLS_ITEM>
<ID>123</ID>
<ID1>1345</ID1>
<SERAIL_NUMBER>1026977­04257</SERAIL_NUMBER>
<PROD_NAME>Plug­in</PROD_NAME>
</QTLS_ITEM>
<QTLS_ITEM>
<ID>123</ID>
<ID1>1345</ID1>
<SERAIL_NUMBER>1026977­04257</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>
1026977­04257 upgrade
1026977­04257 Plug­in
1026977­04257 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>

XSLT check node availability in

I am using XSLT 1.0 in my project. In my XSLT transformation I have to check for a specific element and if that exists - I have to perform some concatenation or else some other concatenation operation.
However, I am not finding an option here, like some built-in function.
Requirement is like
<Root>
<a></a>
<b></b>
<c></c>
</Root>
Here is element <a>, come in request payload, then we need to perform concatenation of <b> and <c> else <c> and <b>.
You can do that with template matching:
<xsl:template match="Root[not(a)]">
<xsl:value-of select="concat(c, b)"/>
</xsl:template>
<xsl:template match="Root[a]">
<xsl:value-of select="concat(b, c)"/>
</xsl:template>
Try it along these lines:
<xsl:template match="/Root">
<xsl:choose>
<xsl:when test="a">
<!-- do something -->
</xsl:when>
<xsl:otherwise>
<!-- do something else -->
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Explanation: the test returns the Boolean value of the node-set selected by the expression a. If the node-set is non-empty, the result is true.
Test for the presence of the element, using xsl:choose
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/Root">
<xsl:choose>
<xsl:when test="a">
<xsl:value-of select="concat(c, b)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(b, c)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Or in a predicate for template matches:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/Root[a]">
<xsl:value-of select="concat(c, b)"/>
</xsl:template>
<xsl:template match="/Root[not(a)]">
<xsl:value-of select="concat(b, c)"/>
</xsl:template>
</xsl:stylesheet>
In your case, use choose and test for the presence of a using boolean() on the according xpath.
<xsl:template match="Root">
<xsl:choose>
<xsl:when test="boolean(./a)">
<xsl:value-of select="concat(./b, ./c)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(./c, ./b)" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>

xslt named template : how to write a template that creates a string representing the xpath of a given node

I'm trying to write a recursive named template that will show the path of a given node:
<?xml version="1.0"?>
<testfile>
<section>
<title>My Section</title>
<para>Trying to write a recursive function that will return a basic xpath of a given node; in the case of this node, I would want to return testfile/section/para, I don't need /testfile/section[1]/para[1] or anything like that. The issue I'm having is that in the case of a named template, I don't know how to select a different node and apply it to the named template.</para>
</section>
</testfile>
I'm trying this template :
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- stylesheet to test a named template trying to build an xpath for a given node -->
<xsl:output method="xml"/>
<xsl:template match="/">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="xpath">
<xsl:call-template name="getXpath">
<xsl:with-param name="pathText" select="''"/>
</xsl:call-template>
</xsl:variable>
<element>element name : <xsl:value-of select="name()"/> path : <xsl:value-of select="$xpath"/></element>
<xsl:apply-templates/>
</xsl:template>
<xsl:template name="getXpath">
<xsl:param name="pathText"/>
<xsl:message>top of get xpath func path text : <xsl:value-of select="$pathText"/> </xsl:message>
<xsl:choose>
<xsl:when test="ancestor::*">
<xsl:message><xsl:value-of select="name()"/> has a parent</xsl:message>
<xsl:call-template name="getXpath">
<xsl:with-param name="pathText">
<xsl:value-of select="name()"/> <xsl:text>/</xsl:text><xsl:value-of select="$pathText"/>
<!-- how to recursively call template with parent node? -->
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:message><xsl:value-of select="name()"/> has no parent!</xsl:message>
<xsl:value-of select="$pathText"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
As per the comment, I'm not sure how to apply a node other than the context node to the named template. The other strategy I tried was to send the node to the template as a param, but I don't know how(or if you can) apply an axis to a param, as in
$thisNode../*
etc.
I'm sure it's something simple that I'm missing...thanks.
You can indeed pass in the node as a param to the template....
<xsl:template name="getXpath">
<xsl:param name="pathText"/>
<xsl:param name="node" select="." />
To apply an axis to it, for example to test for ancestors, you would do this....
<xsl:when test="$node/ancestor::*">
And to pass its parent element to the template when you recursively call it, do this:
<xsl:with-param name="node" select="$node/parent::*" />
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="/">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="xpath">
<xsl:call-template name="getXpath">
<xsl:with-param name="pathText" select="''"/>
</xsl:call-template>
</xsl:variable>
<element>element name : <xsl:value-of select="name()"/> path : <xsl:value-of select="$xpath"/></element>
<xsl:apply-templates/>
</xsl:template>
<xsl:template name="getXpath">
<xsl:param name="pathText"/>
<xsl:param name="node" select="." />
<xsl:message>top of get xpath func path text : <xsl:value-of select="$pathText"/> </xsl:message>
<xsl:choose>
<xsl:when test="$node/ancestor::*">
<xsl:message><xsl:value-of select="name($node)"/> has a parent</xsl:message>
<xsl:call-template name="getXpath">
<xsl:with-param name="pathText">
<xsl:value-of select="name($node)"/> <xsl:text>/</xsl:text><xsl:value-of select="$pathText"/>
</xsl:with-param>
<xsl:with-param name="node" select="$node/parent::*" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:message><xsl:value-of select="name($node)"/> has no parent!</xsl:message>
<xsl:value-of select="$pathText"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
An alternate approach is to use xsl:apply-templates, but with the mode parameter to keep it separate from other template matches. Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="/">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="xpath">
<xsl:apply-templates select="." mode="getXpath">
<xsl:with-param name="pathText" select="''"/>
</xsl:apply-templates>
</xsl:variable>
<element>element name : <xsl:value-of select="name()"/> path : <xsl:value-of select="$xpath"/></element>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="*" mode="getXpath">
<xsl:param name="pathText"/>
<xsl:message>top of get xpath func path text : <xsl:value-of select="$pathText"/> </xsl:message>
<xsl:choose>
<xsl:when test="ancestor::*">
<xsl:message><xsl:value-of select="name()"/> has a parent</xsl:message>
<xsl:apply-templates select=".." mode="getXpath">
<xsl:with-param name="pathText">
<xsl:value-of select="name()"/> <xsl:text>/</xsl:text><xsl:value-of select="$pathText"/>
</xsl:with-param>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:message><xsl:value-of select="name()"/> has no parent!</xsl:message>
<xsl:value-of select="$pathText"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
You shouldn't have to pass a node as a param if you just do an xsl:for-each.
Here's a modified example of your XSLT. (Notice that the positional predicates are only output in the path if they are needed.)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- stylesheet to test a named template trying to build an xpath for a given node -->
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="xpath">
<xsl:call-template name="getXpath"/>
</xsl:variable>
<element>element name : <xsl:value-of select="name()"/> path : <xsl:value-of select="$xpath"/></element>
<xsl:apply-templates/>
</xsl:template>
<xsl:template name="getXpath">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="concat('/',local-name())"/>
<!--Predicate is only output when needed.-->
<xsl:if test="(preceding-sibling::*|following-sibling::*)[local-name()=local-name(current())]">
<xsl:value-of select="concat('[',count(preceding-sibling::*[local-name()=local-name(current())])+1,']')"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
Output (using the input from the question)
<result>
<element>element name : testfile path : /testfile</element>
<element>element name : section path : /testfile/section</element>
<element>element name : title path : /testfile/section/title</element>
<element>element name : para path : /testfile/section/para</element>
</result>
For what it's worth, I wrote a simple XPath generation template about a decade ago, in part 2 of my "styling stylesheets" article on DeveloperWorks:
Listing 4. Template that generates a Pseudo XPath in XSLT
<xsl:template name="pseudo-xpath-to-current-node">
<!-- Special-case for the root node, which otherwise
wouldn't generate any path at all. A bit of a kluge,
but it's simple and efficient. -->
<xsl:if test="not(parent::node())">
<xsl:text>/</xsl:text>
</xsl:if>
<xsl:for-each select="ancestor-or-self::node()">
<xsl:choose>
<xsl:when test="not(parent::node())">
<!-- This clause recognizes the root node, which doesn't need
to be explicitly represented in the XPath. -->
</xsl:when>
<xsl:when test="self::text()">
<xsl:text>/text()[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::comment()">
<xsl:text>/comment()[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::processing-instruction()">
<xsl:text>/processing-instruction()[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::*">
<!-- This test for Elements works because the Principal
Node Type of the self:: axis happens to be Element.
-->
<xsl:text>/</xsl:text>
<xsl:value-of select="name(.)"/>
<xsl:text>[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::node()[name()='xmlns' | starts-with(name(),'xmlns:')]">
<!-- This recognizes namespace nodes, though it's a bit
ugly. XSLT 1.0 doesn't seem to have a more elegant
test. XSLT 2.0 is expected to deprecate the whole
concept of namespace nodes, so it may become a moot
point.
NS nodes are unique; a count isn't required. -->
<xsl:text>/namespace::</xsl:text>
<xsl:value-of select="local-name(.)"/>
</xsl:when>
<xsl:otherwise>
<!-- If I've reached this clause, the node must be an
attribute. Attributes are unique; a count is not
required. -->
<xsl:text>/#</xsl:text>
<xsl:value-of select="name(.)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
That was an XSLT 1.0 solution, structured for clarity. It's probably possible to simplify it, especially if you're using XSLT and XPath 2.0.
As I explained there, this "pseudo-XPath" version ignores the namespace issue, since I didn't need it for that proof-of-concept tool and since it was intended for human-readable messages rather than for execution. It could be corrected to manage namespaces properly by changing it to write out paths that specify node type with a predicate explicitly testing localname and namespace URI. The resulting paths would be bulkier and harder for humans to process. Exercise for the reader, if you're so inclined.
You might also be able to replace the positional index with something more expressive... but knowing what's going to be meaningful is not easy.
Hope that helps. Have fun.
(Oh, almost forgot: I wouldn't be surprised if there are other solutions on the XSLT FAQ site.)
I think you want something like this:
<xsl:variable name="get.path">
<xsl:text> /</xsl:text>
<xsl:for-each select="ancestor-or-self::*">
<xsl:variable name="get.current.node" select="name(.)"/>
<xsl:value-of select="name()"/>
<xsl:text>[</xsl:text>
<xsl:value-of select="count(preceding-sibling::*[name(.) = $get.current.node]) + 1"/>
<xsl:text>]</xsl:text>
<xsl:if test="position() != last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>

How to apply a function to a sequence of nodes in XSLT

I need to write an XSLT function that transforms a sequence of nodes into a sequence of strings. What I need to do is to apply a function to all the nodes in the sequence and return a sequence as long as the original one.
This is the input document
<article id="4">
<author ref="#Guy1"/>
<author ref="#Guy2"/>
</article>
This is how the calling site:
<xsl:template match="article">
<xsl:text>Author for </xsl:text>
<xsl:value-of select="#id"/>
<xsl:variable name="names" select="func:author-names(.)"/>
<xsl:value-of select="string-join($names, ' and ')"/>
<xsl:value-of select="count($names)"/>
</xsl:function>
And this is the code of the function:
<xsl:function name="func:authors-names">
<xsl:param name="article"/>
<!-- HELP: this is where I call `func:format-name` on
each `$article/author` element -->
</xsl:function>
What should I use inside func:author-names? I tried using xsl:for-each but the result is a single node, not a sequence.
<xsl:sequence select="$article/author/func:format-name(.)"/> is one way, the other is <xsl:sequence select="for $a in $article/author return func:format-name($a)"/>.
I am not sure you would need the function of course, doing
<xsl:value-of select="author/func:format-name(.)" separator=" and "/>
in the template of article should do.
If only a sequence of #ref values should be generated there is no need for a function or xsl version 2.0.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" />
<xsl:template match="article">
<xsl:apply-templates select="author" />
</xsl:template>
<xsl:template match="author">
<xsl:value-of select="#ref"/>
<xsl:if test="position() !=last()" >
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:template>
</xsl:styleshee
This will generate:
#Guy1,#Guy2
Update:
Do have the string join by and and have a count of items. Try this:
<xsl:template match="article">
<xsl:text>Author for </xsl:text>
<xsl:value-of select="#id"/>
<xsl:apply-templates select="author" />
<xsl:value-of select="count(authr[#ref])"/>
</xsl:template>
<xsl:template match="author">
<xsl:value-of select="#ref"/>
<xsl:if test="position() !=last()" >
<xsl:text> and </xsl:text>
</xsl:if>
</xsl:template>
With this output:
Author for 4#Guy1 and #Guy20

Grouping on Multiple Field

I am generating XML file from Database as below...
<?xml version = '1.0'?>
<T0019>
<IFTA_ACCOUNT>
<IFTA_CARRIER_ID_NUMBER>705</IFTA_CARRIER_ID_NUMBER>
<IFTA_LICENSE_NUMBER>631227666</IFTA_LICENSE_NUMBER>
<IFTA_BASE_COUNTRY>US</IFTA_BASE_COUNTRY>
<IFTA_BASE_STATE>AL</IFTA_BASE_STATE>
<IFTA_STATUS_CODE>0 </IFTA_STATUS_CODE>
<IFTA_STATUS_DATE>2009-01-01</IFTA_STATUS_DATE>
<IFTA_ISSUE_DATE>2009-01-01</IFTA_ISSUE_DATE>
<IFTA_EXPIRE_DATE>2009-12-01</IFTA_EXPIRE_DATE>
<IFTA_UPDATE_DATE>2008-12-30</IFTA_UPDATE_DATE>
<NAME_TYPE>LG</NAME_TYPE>
<NAME>K D L TRUCKING INC</NAME>
<ADDRESS_TYPE>PH</ADDRESS_TYPE>
<STREET_LINE_1>200 MARTIN LANE</STREET_LINE_1>
<CITY>OHATCHEE</CITY>
<STATE>AL</STATE>
<ZIP_CODE>36271</ZIP_CODE>
<COUNTY>CALHOUN COUNTY</COUNTY>
<COUNTRY>US</COUNTRY>
</IFTA_ACCOUNT>
<IFTA_ACCOUNT>
<IFTA_CARRIER_ID_NUMBER>705</IFTA_CARRIER_ID_NUMBER>
<IFTA_LICENSE_NUMBER>631227666</IFTA_LICENSE_NUMBER>
<IFTA_BASE_COUNTRY>US</IFTA_BASE_COUNTRY>
<IFTA_BASE_STATE>AL</IFTA_BASE_STATE>
<IFTA_STATUS_CODE>0 </IFTA_STATUS_CODE>
<IFTA_STATUS_DATE>2009-01-01</IFTA_STATUS_DATE>
<IFTA_ISSUE_DATE>2009-01-01</IFTA_ISSUE_DATE>
<IFTA_EXPIRE_DATE>2009-12-01</IFTA_EXPIRE_DATE>
<IFTA_UPDATE_DATE>2008-12-30</IFTA_UPDATE_DATE>
<NAME_TYPE>LG</NAME_TYPE>
<NAME>K D L TRUCKING INC</NAME>
<ADDRESS_TYPE>MA</ADDRESS_TYPE>
<STREET_LINE_1>200 MARTIN LANE</STREET_LINE_1>
<CITY>OHATCHEE</CITY>
<STATE>AL</STATE>
<ZIP_CODE>36271</ZIP_CODE>
<COUNTRY>US</COUNTRY>
</IFTA_ACCOUNT>
</T0019>
I have taken first two records from generated XSLT.
With the use of XSLT I have tried lot to group record on basis of
IFTA_LICENSE_NUMBER,IFTA_BASE_COUNTRY,IFTA_BASE_ST ATE,NAME_TYPE,ADDRESS_TYPE but i failed to generated XML like this..
<?xml version="1.0" encoding="UTF-8" ?>
<T0019>
<IFTA_ACCOUNT>
<IFTA_CARRIER_ID_NUMBER>705</IFTA_CARRIER_ID_NUMBER>
<IFTA_BASE_COUNTRY>US</IFTA_BASE_COUNTRY>
<IFTA_BASE_STATE>AL</IFTA_BASE_STATE>
<IFTA_LICENSE_NUMBER>631227666</IFTA_LICENSE_NUMBER>
<IFTA_STATUS_CODE>0</IFTA_STATUS_CODE>
<IFTA_STATUS_DATE>2009-01-01</IFTA_STATUS_DATE>
<IFTA_ISSUE_DATE>2009-01-01</IFTA_ISSUE_DATE>
<IFTA_EXPIRE_DATE>2009-12-01</IFTA_EXPIRE_DATE>
<IFTA_UPDATE_DATE>2008-12-30</IFTA_UPDATE_DATE>
<IFTA_NAME>
<NAME_TYPE>LG</NAME_TYPE>
<NAME>K D L TRUCKING INC</NAME>
<IFTA_ADDRESS>
<ADDRESS_TYPE>PH</ADDRESS_TYPE>
<STREET_LINE_1>200 MARTIN LANE</STREET_LINE_1>
<STREET_LINE_2 />
<CITY>OHATCHEE</CITY>
<STATE>AL</STATE>
<ZIP_CODE>36271</ZIP_CODE>
<COUNTY>CALHOUN COUNTY</COUNTY>
<COUNTRY>US</COUNTRY>
</IFTA_ADDRESS>
<IFTA_ADDRESS>
<ADDRESS_TYPE>MA</ADDRESS_TYPE>
<STREET_LINE_1>200 MARTIN LANE</STREET_LINE_1>
<STREET_LINE_2 />
<CITY>OHATCHEE</CITY>
<STATE>AL</STATE>
<ZIP_CODE>36271</ZIP_CODE>
<COUNTY />
<COUNTRY>US</COUNTRY>
</IFTA_ADDRESS>
</IFTA_NAME>
</IFTA_ACCOUNT>
</T0019>
Your example input records have two different ADDRESS_TYPE values, so if you were grouping by ADDRESS_TYPE, these two would be distinct. And since your output also shows the two distinct values, I assume that you don't really want ADDRESS_TYPE in your list of grouping keys. Is that the real problem?
The XSLT spec gives two strategies for composite keys. When I tried the 'concat' strategy with your data, and with your list of keys minus ADDRESS_TYPE, it grouped these two records together.
Thanks , I have applied following XSLT to solve this Problem.
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="T0019">
<xsl:copy>
<xsl:for-each-group select="IFTA_ACCOUNT" group-by="IFTA_LICENSE_NUMBER">
<xsl:for-each-group select="current-group()" group-by="IFTA_BASE_COUNTRY">
<xsl:for-each-group select="current-group()" group-by="IFTA_BASE_STATE">
<IFTA_ACCOUNT>
<xsl:apply-templates select="IFTA_CARRIER_ID_NUMBER|IFTA_BASE_COUNTRY|IFTA_BASE_STATE|IFTA_LICENSE_NUMBER|IFTA_STATUS_CODE|IFTA_STATUS_DATE|IFTA_ISSUE_DATE|IFTA_EXPIRE_DATE|IFTA_UPDATE_DATE"/>
<xsl:for-each-group select="current-group()" group-by="NAME_TYPE">
<IFTA_NAME>
<xsl:apply-templates select="NAME_TYPE|NAME"/>
<xsl:for-each select="current-group()">
<IFTA_ADDRESS>
<xsl:apply-templates select="ADDRESS_TYPE|STREET_LINE_1|STREET_LINE_2|CITY|STATE|ZIP_CODE|COUNTY|COUNTRY"/>
</IFTA_ADDRESS>
</xsl:for-each>
</IFTA_NAME>
</xsl:for-each-group>
</IFTA_ACCOUNT>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="IFTA_CARRIER_ID_NUMBER">
<IFTA_CARRIER_ID_NUMBER>
<xsl:value-of select="."/>
</IFTA_CARRIER_ID_NUMBER>
</xsl:template>
<xsl:template match="IFTA_LICENSE_NUMBER">
<IFTA_LICENSE_NUMBER>
<xsl:value-of select="."/>
</IFTA_LICENSE_NUMBER>
</xsl:template>
<xsl:template match="IFTA_BASE_COUNTRY">
<IFTA_BASE_COUNTRY>
<xsl:value-of select="."/>
</IFTA_BASE_COUNTRY>
</xsl:template>
<xsl:template match="IFTA_BASE_STATE">
<IFTA_BASE_STATE>
<xsl:value-of select="."/>
</IFTA_BASE_STATE>
</xsl:template>
<xsl:template match="IFTA_STATUS_CODE">
<IFTA_STATUS_CODE>
<xsl:value-of select="."/>
</IFTA_STATUS_CODE>
</xsl:template>
<xsl:template match="IFTA_STATUS_DATE">
<IFTA_STATUS_DATE>
<xsl:value-of select="."/>
</IFTA_STATUS_DATE>
</xsl:template>
<xsl:template match="IFTA_ISSUE_DATE">
<IFTA_ISSUE_DATE>
<xsl:value-of select="."/>
</IFTA_ISSUE_DATE>
</xsl:template>
<xsl:template match="IFTA_EXPIRE_DATE">
<IFTA_STATUS_DATE>
<xsl:value-of select="."/>
</IFTA_STATUS_DATE>
</xsl:template>
<xsl:template match="IFTA_UPDATE_DATE">
<IFTA_ISSUE_DATE>
<xsl:value-of select="."/>
</IFTA_ISSUE_DATE>
</xsl:template>
<xsl:template match="NAME_TYPE">
<NAME_TYPE>
<xsl:value-of select="."/>
</NAME_TYPE>
</xsl:template>
<xsl:template match="NAME">
<NAME_TYPE>
<xsl:value-of select="."/>
</NAME_TYPE>
</xsl:template>
<xsl:template match="ADDRESS_TYPE">
<ADDRESS_TYPE>
<xsl:value-of select="."/>
</ADDRESS_TYPE>
</xsl:template>
<xsl:template match="STREET_LINE_1">
<STREET_LINE_1>
<xsl:value-of select="."/>
</STREET_LINE_1>
</xsl:template>
<xsl:template match="STREET_LINE_2">
<STREET_LINE_2>
<xsl:value-of select="."/>
</STREET_LINE_2>
</xsl:template>
<xsl:template match="CITY">
<CITY>
<xsl:value-of select="."/>
</CITY>
</xsl:template>
<xsl:template match="STATE">
<STATE>
<xsl:value-of select="."/>
</STATE>
</xsl:template>
<xsl:template match="ZIP_CODE">
<ZIP_CODE>
<xsl:value-of select="."/>
</ZIP_CODE>
</xsl:template>
<xsl:template match="COUNTY">
<COUNTY>
<xsl:value-of select="."/>
</COUNTY>
</xsl:template>
<xsl:template match="COUNTRY">
<COUNTRY>
<xsl:value-of select="."/>
</COUNTRY>
</xsl:template>
</xsl:stylesheet>
Thanks to all for your wonderful help.