Search code examples
xsltxslt-1.0xslt-groupingmuenchian-grouping

How keys work in muenchian grouping


I haven't found an answer yet on how keys are generated while doing muenchian-grouping in xslt. The examples I found all have very simple xml files. Given the following xml-file:

<?xml version="1.0" encoding="UTF-8"?>
<despatch-advice>
<message-id>2012041715435517181</message-id>
<message-creation>2012-04-17T15:43:55.000+02:00</message-creation>
<originatingFlow>DespatchAdvice</originatingFlow>
<shipment>
    <transport-mode>Sea</transport-mode>
    <transport-id>1111</transport-id>
    <order-line>
        <order>123</order>
        <order-date>2012-01-17+01:00</order-date>
        <selling-code>ME</selling-code>
    </order-line>
</shipment>
<shipment>
    <transport-mode>Sea</transport-mode>
    <transport-id>2222</transport-id>
    <order-line>
        <order>456</order>
        <order-date>2012-01-17+01:00</order-date>
        <selling-code>ME</selling-code>
    </order-line>
    <order-line>
        <order>789</order>
        <order-date>2012-01-17+01:00</order-date>
        <selling-code>ME</selling-code>
    </order-line>
    <order-line>
        <order>832</order>
        <order-date>2012-01-17+01:00</order-date>
        <selling-code>XM</selling-code>
    </order-line>
</shipment>
<shipment>
    <transport-mode>Air</transport-mode>
    <transport-id>333</transport-id>
    <order-line>
        <order>781</order>
        <order-date>2012-01-17+01:00</order-date>
        <selling-code>XM</selling-code>
    </order-line>
    <order-line>
        <order>789</order>
        <order-date>2012-01-17+01:00</order-date>
        <selling-code>XM</selling-code>
    </order-line>
</shipment>

What I would like to do is group the order-lines per selling-code in each shipment.

In my xslt file I define the key as follows:

<xsl:key name="groups" match="order-line" use="selling-code"/>

And after having created the first part of the xml-file I do the following:

<xsl:for-each select="order-line[generate-id(.)=generate-id(key('groups',selling-code)[1])]"> 
<ns0:order-line-group>                                      
<xsl:for-each select="key('groups',selling-code)">
<ns0:order-line>

I would expect that the xml file would be parsed from the current <shipment> node and the keys built will only use the <order-line> nodes for that specific shipment. But instead I get three <shipment> nodes containg ALL <order-line> nodes in the file. So it seems that when the key is created the xml file is always parsed from the root? Is that so? Is there a way around this?


Solution

  • You need to define a key like

    <xsl:key name="groups" match="order-line" use="concat(generate-id(..), '|', selling-code)"/>
    

    and then you need to use

    <xsl:for-each select="order-line[generate-id(.)=generate-id(key('groups',concat(generate-id(..), '|', selling-code))[1])]"> 
    <ns0:order-line-group>                                      
    <xsl:for-each select="key('groups',concat(generate-id(..), '|', selling-code))">
    <ns0:order-line>
    

    to ensure you only process items for each shipment.

    With XSLT 2.0 the key function has a third argument to look for items in a subtree but XSLT 1.0 does not have that, so you need to put the id of the node you want to restrict the search to in the key value. And of course XSLT 2.0 has for-each-group so for grouping you don't need keys at all.