How do a for-each in this xsl? - xslt

I get this result from a service in a Bpel process.
How I do a For-each with OSTypeOutput and inside of it
test the value of "serv"?
I'am trying to do a transformation.
<siOutPut>
-<OOPreOutput xmlns="http://my.web.com/test/types">
-<OSTypes xmlns:ns1="http://my.web.com/test/servtp">
-<ns1:OSTypeOutput>
<ns1:serv>A1</ns1:serv>
<ns1:serv>A2</ns1:serv>
<ns1:serv>A3</ns1:serv>
</ns1:OSTypeOutput>
</OSTypes>
</OOPreOutput>
</siOutPut>
I have tried this way but I can't get nothing:
<xsl:param name="Param">
<xsl:text disable-output-escaping="no">A3</xsl:text>
</xsl:param>
<xsl:template match="/">
<ns1:GetTestResponse>
<ns1:MyId>
<xsl:for-each select="/ns1:OOPreOutput/ns1:OSTypes/ns1:OSTypeOutput">
<xsl:if test="(./serv = $Param)">
<xsl:value-of select="'Found'"/>
</xsl:if>
</xsl:for-each>
</ns1:MyId>
</ns1:GetTestResponse>
</xsl:template>
I've made this test:
<ns1:MyId>
<xsl:for-each select="/ns1:OOPreOutput/ns1:OSTypes">
<xsl:value-of select="current()"/>
</xsl:for-each>
</ns1:MyId>
And get this result :
A1A2A3
I also did this test but without result :
<xsl:param name="Param">
<xsl:text disable-output-escaping="no">A3</xsl:text>
</xsl:param>
<xsl:template match="/">
<ns1:GetTestResponse>
<ns1:MyId>
<xsl:if test="/ns1:OOPreOutput/ns1:OSTypes/ns1:OSTypeOutput/ns1:serv = $Param">
<xsl:value-of select="'Found'"/>
</xsl:if>
</ns1:MyId>
</ns1:GetTestResponse>
</xsl:template>
I changed a little bit the output:
-<siOutPut>
-<OOPreOutput xmlns="http://int.clear.com/types">
+<Deliv></Deliv>
<OSTypes>
<ns1:serv xmlns:ns1="http://my.test.com/web/services">A1</ns1:serv>
<ns1:serv xmlns:ns1="http://my.test.com/web/services">A2</ns1:serv>
<ns1:serv xmlns:ns1="http://my.test.com/web/services">A3</ns1:serv>
</OSTypes>
</OOPreOutput>
</siOutPut>
How I should verify if ns1:serv has "A3" value?

You don't have one of the namespaces declared in the stylesheet. Re-writing the stylesheet:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns1="http://my.web.com/test/servtp" xmlns:ns2="http://my.web.com/test/types" version="1.0">
<xsl:param name="Param">
<xsl:text disable-output-escaping="no">A3</xsl:text>
</xsl:param>
<xsl:template match="/">
<ns1:GetTestResponse>
<ns1:MyId>
<xsl:for-each select="/siOutPut/ns2:OOPreOutput/ns2:OSTypes/ns1:OSTypeOutput">
<xsl:if test="(./ns1:serv = $Param)">
<xsl:value-of select="'Found'"/>
</xsl:if>
</xsl:for-each>
</ns1:MyId>
</ns1:GetTestResponse>
</xsl:template>
</xsl:stylesheet>
But, there is no need for doing a for-each as your just want to check the existence of "serv" with some value.
It can be done this way too:
<xsl:template match="/">
<ns1:GetTestResponse>
<ns1:MyId>
<xsl:if test="siOutPut/ns2:OOPreOutput/ns2:OSTypes/ns1:OSTypeOutput/ns1:serv = $Param">
<xsl:value-of select="'Found'"/>
</xsl:if>
</ns1:MyId>
</ns1:GetTestResponse>
</xsl:template>

I'd try to simplify the logic by using apply templates. I think the problem may be < xsl:value-of select="'Found'"/>. I'd say it's < xsl:text>'Found'< /xsl:text>. And I think you didn't match < siOutPut>. I don't think < xsl:template match="/"> solves it.
<xsl:param name="Param">
<xsl:text disable-output-escaping="no">A3</xsl:text>
</xsl:param>
<xsl:template match="/">
<ns1:GetTestResponse>
<ns1:MyId>
<xsl:apply-templates />
</ns1:MyId>
</ns1:GetTestResponse>
</xsl:template>
<xsl:template match="ns1:OSTypeOutput">
<xsl:for-each select="ns1:serv">
<xsl:if test=".= $Param">
<xsl:text>'Found'</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>

After changing the output I made this for-each and it Worked:
<xsl:for-each select="/ns2:OOPreOutput/ns2:OSTypes">
<xsl:if test="(./ns1:serv = $Param)">
<xsl:value-of select="'Found'"/>
</xsl:if>
</xsl:for-each>
And also doing the Direct test as suggested Lingamurthy
<xsl:if test="ns2:OOPreOutput/ns2:OSTypes/ns1:serv = $Param">
<xsl:value-of select="'Found'"/>
<xsl:if>
Thank you all.

Related

How to pass as variable into .xslt 1.0 key expressions

Following on from my original question:
Filtering, Grouping, Counting and Selecting specific nodes in XML using XSLT 1.0
I've now run into another problem. Using this code that was kindly provided by michael.hor257k
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:key name="vehicle-by-series" match="Vehicle[Model='KA']" use="Series" />
<xsl:template match="/Dealer">
<xsl:for-each select="Vehicle[Model='KA'][count(. | key('vehicle-by-series', Series)[1]) = 1]">
<xsl:value-of select="Model"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="Series"/>
<xsl:text>, </xsl:text>
<xsl:variable name="grp" select="key('vehicle-by-series', Series)" />
<xsl:value-of select="count($grp)"/>
<xsl:text> in stock, starting from </xsl:text>
<xsl:for-each select="$grp">
<xsl:sort select="Price" data-type="number" order="ascending"/>
<xsl:if test="position() = 1">
<xsl:value-of select="Price"/>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I'm passing in the parameter model:
<xsl:param name="model" />
From an asp.NET URL variable.
I want to substitute the currently hardcoded KA for the model parameter. From the searching I've done you can't use parameters or variables in a key expression in XSLT 1.0. And I don't have the option to upgrade to 2.0. Mores the pity.
Would really appreciate some more help please.
Thanks in advance.
I would suggest you do:
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:param name="model"/>
<xsl:key name="vehicle-by-series" match="Vehicle" use="concat(Model, '|', Series)" />
<xsl:template match="/Dealer">
<xsl:for-each select="Vehicle[Model=$model][count(. | key('vehicle-by-series', concat(Model, '|', Series))[1]) = 1]">
<xsl:value-of select="Model"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="Series"/>
<xsl:text>, </xsl:text>
<xsl:variable name="grp" select="key('vehicle-by-series', concat(Model, '|', Series))" />
<xsl:value-of select="count($grp)"/>
<xsl:text> in stock, starting from </xsl:text>
<xsl:for-each select="$grp">
<xsl:sort select="Price" data-type="number" order="ascending"/>
<xsl:if test="position() = 1">
<xsl:value-of select="Price"/>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

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

Removing the root element using xslt

I have an xml schema that has a structure like
<Level>
<Level1>...data...</Level1>
<Level2>...data...</Level2>
.
.
.
</Level>
I want to remove the <Level> tag. How am I supposed to do that, with the help of xslt.
The standard answer to any "how do I keep most of my XML the same but tweak some little bits of it" question is "use an identity template and then override it for the specific things you want to change"
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- omit the <?xml?> line in the output, it won't be well-formed anyway -->
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()" /></xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:apply-templates select="node()" />
</xsl:template>
</xsl:stylesheet>
but as Mr Lister points out in the comments, this will leave you with something that is not well formed XML, as it will have more than one document element.
When I apply this stylesheet on the input XML
<Level>
<Level1>...data...</Level1>
<Level2>...data...</Level2>
</Level>
it produces the result
<Level1>...data...</Level1>
<Level2>...data...</Level2>
If you want to store all child elements of the document element in a variable, you can do something like:
<xsl:variable name="myVar" select="/*/*"/>
If, however, you want to the stylesheet to produce a string, this might be a solution:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="*">
<!-- We write the opening tag -->
<xsl:value-of select="concat('<',local-name())"/>
<!-- Now, all attributes -->
<xsl:apply-templates select="#*"/>
<!-- Depending on whether we have an empty element or not,
we're adding the content or closing it immediately -->
<xsl:choose>
<xsl:when test="node()">
<!-- Close opening tag -->
<xsl:value-of select="'>'"/>
<!-- Add the content -->
<xsl:apply-templates select="node()"/>
<!-- Write closing tag -->
<xsl:value-of select="concat('</',local-name(),'>')"/>
</xsl:when>
<xsl:otherwise>
<!-- Create empty element by closing tag immediately -->
<xsl:value-of select="'/>'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#*">
<!-- Write an attribute -->
<xsl:value-of select="concat(' ',local-name(),'="',.,'"')"/>
</xsl:template>
</xsl:stylesheet>
It produces text (and therefore you won't get non-well-formed XML). It's a little over-simplified because it does not handle namespaces, comments, processing instructions and quotes in attributes. If your input XML contains any of these, you'd have to refine the stylesheet.
I have created a new XSLT definition which fulfill my requremtn with the help of your code
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" method="text" omit-xml-declaration="yes"/>
<xsl:variable name="nl">
<xsl:text/>
</xsl:variable>
<xsl:variable name="tb">
<xsl:text/>
</xsl:variable>
<xsl:template match="/*">
<!-- Open the root array -->
<xsl:text>[{</xsl:text>
<xsl:value-of select="$nl"/>
<!-- Process all the child nodes of the root -->
<xsl:apply-templates mode="detect" select="*">
<xsl:with-param name="indent" select="$tb"/>
</xsl:apply-templates>
<!-- Close the root array -->
<xsl:value-of select="$nl"/>
<xsl:text>}]</xsl:text>
</xsl:template>
<xsl:template match="*" mode="detect">
<xsl:choose>
<xsl:when test="name(preceding-sibling::*[1]) = name(current()) and name(following-sibling::*[1]) != name(current())">
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:text>]</xsl:text>
<xsl:if test="count(following-sibling::*[name() != name(current())]) > 0">, </xsl:if>
</xsl:when>
<xsl:when test="name(preceding-sibling::*[1]) = name(current())">
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:if test="name(following-sibling::*) = name(current())">, </xsl:if>
</xsl:when>
<xsl:when test="following-sibling::*[1][name() = name(current())]">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>" : [</xsl:text>
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:text>, </xsl:text>
</xsl:when>
<xsl:when test="count(./child::*) > 0 or count(#*) > 0">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : [<xsl:apply-templates
mode="obj-content" select="."/>
<xsl:if test="count(following-sibling::*) > 0">], </xsl:if>
</xsl:when>
<xsl:when test="count(./child::*) = 0">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:apply-templates select="."/>
<xsl:text>"</xsl:text>
<xsl:if test="count(following-sibling::*) > 0">, </xsl:if>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="*" mode="obj-content">
<xsl:text>{</xsl:text>
<xsl:apply-templates mode="attr" select="#*"/>
<xsl:if test="count(#*) > 0 and (count(child::*) > 0 or text())">, </xsl:if>
<xsl:apply-templates mode="detect" select="./*"/>
<xsl:if test="count(child::*) = 0 and text() and not(#*)">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:value-of select="text()"/>
<xsl:text>"</xsl:text>
</xsl:if>
<xsl:if test="count(child::*) = 0 and text() and #*">
<xsl:text>: "</xsl:text>
<xsl:value-of select="text()"/>
<xsl:text>"</xsl:text>
</xsl:if>
<xsl:text>}</xsl:text>
<xsl:if test="position() < last()">, </xsl:if>
</xsl:template>
<xsl:template match="#*" mode="attr">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:value-of select="."/>
<xsl:text>"</xsl:text>
<xsl:if test="position() < last()">,</xsl:if>
</xsl:template>
<xsl:template match="node/#TEXT | text()" name="removeBreaks">
<xsl:param name="pText" select="normalize-space(.)"/>
<xsl:choose>
<xsl:when test="not(contains($pText, '
'))">
<xsl:copy-of select="$pText"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(substring-before($pText, '
'), ' ')"/>
<xsl:call-template name="removeBreaks">
<xsl:with-param name="pText" select="substring-after($pText, '
')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

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.