 |
| XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead. |
Welcome to the p2p.wrox.com Forums.
You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
|
|
|
|

March 9th, 2016, 07:07 AM
|
|
Authorized User
|
|
Join Date: Nov 2015
Posts: 12
Thanks: 4
Thanked 0 Times in 0 Posts
|
|
Group XML message based on 3 keys
Input XML
<Positions>
<Position>
<cust_HireType/>
<location>London</location>
<department/>
<cust_TCCID/>
<cust_RegulatedRole/>
<code>001000090</code>
<effectiveStatus>A</effectiveStatus>
<costCenter>18845-3761</costCenter>
</Position>
<Position>
<cust_HireType>Inv</cust_HireType>
<location>London</location>
<department>25092089</department>
<cust_TCCID/>
<cust_RegulatedRole>no</cust_RegulatedRole>
<code>001000090</code>
<effectiveStatus>A</effectiveStatus>
<costCenter>18845-3761</costCenter>
</Position>
<Position>
<cust_HireType>Inv</cust_HireType>
<location>London</location>
<department>25092089</department>
<cust_TCCID/>
<cust_RegulatedRole>no</cust_RegulatedRole>
<code>001000090</code>
<effectiveStatus>A</effectiveStatus>
<costCenter>18845-3761</costCenter>
</Position>
<Position>
<cust_HireType>Inv</cust_HireType>
<location>Nigeria</location>
<department>25092089</department>
<cust_TCCID/>
<cust_RegulatedRole>no</cust_RegulatedRole>
<code>001000078</code>
<effectiveStatus>A</effectiveStatus>
<costCenter>18845-3761</costCenter>
</Position>
<Position>
<cust_HireType>Inv</cust_HireType>
<location>Paris</location>
<department>25092089</department>
<cust_TCCID/>
<cust_RegulatedRole>no</cust_RegulatedRole>
<code>001000067</code>
<effectiveStatus>A</effectiveStatus>
<costCenter>18845-3761</costCenter>
</Position>
</Positions>
Output XML:
<Positions>
<Position>
<cust_HireType/>
<location>London</location>
<department/>
<cust_TCCID/>
<cust_RegulatedRole/>
<code>001000090</code>
<effectiveStatus>A</effectiveStatus>
<costCenter>18845-3761</costCenter>
<NOC>2</NOC>
</Position>
<Position>
<cust_HireType>Inv</cust_HireType>
<location>London</location>
<department>25092089</department>
<cust_TCCID/>
<cust_RegulatedRole>no</cust_RegulatedRole>
<code>001000090</code>
<effectiveStatus>A</effectiveStatus>
<costCenter>18845-3761</costCenter>
<NOC>0</NOC>
</Position>
<Position>
<cust_HireType>Inv</cust_HireType>
<location>London</location>
<department>25092089</department>
<cust_TCCID/>
<cust_RegulatedRole>no</cust_RegulatedRole>
<code>001000090</code>
<effectiveStatus>A</effectiveStatus>
<costCenter>18845-3761</costCenter>
<NOC>0</NOC>
</Position>
<NOP>3</NOP>
</Positions>
Requirement: Need to group XML based on 3 key values which is location,code and cost center in the input file. The output should have all the fields as the input XML with 1 extra tag added in the first position tag with value as total number of similar positions -1 and the other second and third position with value as 0. And an additional tag with total number of position tags with similar keys which is in this case is 3.
|
|

March 9th, 2016, 07:19 AM
|
 |
Wrox Author
|
|
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
|
|
This looks like a pretty standard grouping question. You haven't made it clear whether you are using XSLT 1.0 or 2.0 (the solutions will be very different), and you haven't given any clues about what you have tried or investigated, so we don't really know where you got stuck. If you're using XSLT 2.0, use xsl:for-each-group; if you're forced to use 1.0, use Muenchian Grouping, which you will find in the index of your favourite Wrox XSLT book.
Neither XSLT 1.0 nor 2.0 offer any special way of handling composite grouping keys, you will need to combine the keys into a single string using string concatenation.
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
|
|

March 9th, 2016, 08:45 AM
|
|
Authorized User
|
|
Join Date: Nov 2015
Posts: 12
Thanks: 4
Thanked 0 Times in 0 Posts
|
|
Hi,
I was trying to do this with single key "location" first but unable to achieve the requirement.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<Positions>
<xsl:for-each select="Positions/Position">
<xsl:variable name="location1">
<xsl:value-of select="location"/>
</xsl:variable>
<Position>
<cust_HireType>
<xsl:value-of select="cust_HireType"/>
</cust_HireType>
<location>
<xsl:call-template name="XXX">
<xsl:with-param name="location1" select="$location1"/>
</xsl:call-template>
</location>
<department>
<xsl:value-of select="department"/>
</department>
<cust_TCCID>
<xsl:value-of select="cust_TCCID"/>
</cust_TCCID>
<cust_RegulatedRole>
<xsl:value-of select="cust_RegulatedRole"/>
</cust_RegulatedRole>
<code>
<xsl:value-of select="code"/>
</code>
<effectiveStatus>
<xsl:value-of select="effectiveStatus"/>
</effectiveStatus>
</Position>
</xsl:for-each>
</Positions>
</xsl:template>
<xsl:template name="XXX">
<xsl:param name="location1"/>
<xsl:for-each select="Positions/Position/location">
<xsl:choose>
<xsl:when test="$location1=location">
<xsl:value-of select="location"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I am using XSLT 2.0 but unable to achieve it.
|
|

March 9th, 2016, 10:24 AM
|
 |
Friend of Wrox
|
|
Join Date: Aug 2007
Posts: 2,128
Thanks: 1
Thanked 189 Times in 188 Posts
|
|
You probably want to use the xsl:for-each-group instruction, something like this:
Code:
<xsl:for-each-group select="Position" group-by="concat(location, '|', code, '|', costCenter)">
...
</xsl:for-each-group>
Then inside this you would want to use the current-group() sequence to output the first element, with the count() - 1, and then the rest with the count = 0.
|
|
The Following User Says Thank You to samjudson For This Useful Post:
|
|
|

March 9th, 2016, 10:31 AM
|
 |
Friend of Wrox
|
|
Join Date: Aug 2007
Posts: 2,128
Thanks: 1
Thanked 189 Times in 188 Posts
|
|
Oh, and final count would be something using the count() and distinct-values() functions.
|
|
The Following User Says Thank You to samjudson For This Useful Post:
|
|
|

March 9th, 2016, 01:33 PM
|
|
Authorized User
|
|
Join Date: Nov 2015
Posts: 12
Thanks: 4
Thanked 0 Times in 0 Posts
|
|
Hi,
Thanks for the response. I tried using the for each group function but unable to achieve the requirement.Please correct me as i am a beginner in XSLT and also elaborate on where to use the count and distinct value functions.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<Positions>
<xsl:for-each-group select="Position" group-by="concat(location, '|', code, '|', costCenter)">
<Position>
<cust_HireType>
<xsl:value-of select="cust_HireType"/>
</cust_HireType>
<location>
<xsl:value-of select="location"/>
</location>
<department>
<xsl:value-of select="department"/>
</department>
<cust_TCCID>
<xsl:value-of select="cust_TCCID"/>
</cust_TCCID>
<cust_RegulatedRole>
<xsl:value-of select="cust_RegulatedRole"/>
</cust_RegulatedRole>
<code>
<xsl:value-of select="code"/>
</code>
<effectiveStatus>
<xsl:value-of select="effectiveStatus"/>
</effectiveStatus>
</Position>
</xsl:for-each-group>
</Positions>
</xsl:template>
</xsl:stylesheet>
|
|

March 10th, 2016, 05:12 AM
|
 |
Friend of Wrox
|
|
Join Date: Aug 2007
Posts: 2,128
Thanks: 1
Thanked 189 Times in 188 Posts
|
|
Inside the xsl:for-each-group you have access to a set, called current-group() that returns you all the nodes in that group.
So you can the perform a xsl:for-each over that group:
Code:
<xsl:for-each select="current-group()">
<Position>
<xsl:apply-templates/> <!-- this defaults to processing all the child nodes) -->
</Position>
</xsl:for-each>
To add the NOC element you would then need to work out if you are in the first element or not, usually using the position() function (if it is equal to 1 you are in the first element).
You could do this using an xsl:choose with an xsl:when and an xsl:otherwise, or you could use xquery:
Code:
<NOC><xsl:value-of select="if(position()=1) then count(current-group()) - 1 else 0"/></NOC>
The function for getting the complete count would be:
Code:
count(distinct-values(Position/concat(location, '|', code, '|', costCenter)))
|
|
 |