xsltxslt-1.0xslt-grouping

xslt, group by node with specific value (multiple nodes with same name in each line) (key problem)


I have an xml that looks like below.

<?xml version="1.0" encoding="UTF-8"?>
<Invoice xsi:schemaLocation="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2 http://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/UBL-Invoice-2.1.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
    <cac:InvoiceLine>
        <cbc:ID></cbc:ID>
        <cbc:LineExtensionAmount currencyID="EUR">93.46</cbc:LineExtensionAmount>
        <cac:Item>
            <cac:AdditionalItemProperty>
                <cbc:Name>Total Hours</cbc:Name>
                <cbc:Value>43</cbc:Value>
            </cac:AdditionalItemProperty>
            <cac:AdditionalItemProperty>
                <cbc:Name>naamKandidaat</cbc:Name>
                <cbc:Value>Kees Netelvrees</cbc:Value>
            </cac:AdditionalItemProperty>
        </cac:Item>
    </cac:InvoiceLine>
    <cac:InvoiceLine>
        <cbc:ID></cbc:ID>
        <cbc:LineExtensionAmount currencyID="EUR">2.77</cbc:LineExtensionAmount>
        <cac:Item>
            <cac:AdditionalItemProperty>
                <cbc:Name>Total Hours</cbc:Name>
                <cbc:Value>43</cbc:Value>
            </cac:AdditionalItemProperty>
             <cac:AdditionalItemProperty>
                **<cbc:Name>naamKandidaat</cbc:Name>**
                <cbc:Value>Jaap Aap</cbc:Value>
            </cac:AdditionalItemProperty>
        </cac:Item>
    </cac:InvoiceLine>
    <cac:InvoiceLine>
        <cbc:ID></cbc:ID>
        <cbc:LineExtensionAmount currencyID="EUR">100.00</cbc:LineExtensionAmount>
        <cac:Item>
            <cac:AdditionalItemProperty>
                <cbc:Name>Total Hours</cbc:Name>
                <cbc:Value>43</cbc:Value>
            </cac:AdditionalItemProperty>
             <cac:AdditionalItemProperty>
                **<cbc:Name>naamKandidaat</cbc:Name>**
                <cbc:Value>Jaap Aap</cbc:Value>
            </cac:AdditionalItemProperty>
        </cac:Item>
    </cac:InvoiceLine>
</Invoice>

I need to group cac:InvoiceLine/cac:Item/cac:AdditionalItemProperty/cbc:Value where InvoiceLine/cac:Item/cac:AdditionalItemProperty/cbc:Name**=naamKandidaat**. The purpose is to sum nodes within cac:InvoiceLine where cbc:Name is the same. The problem is that I do not know how to create the key

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:ccts="urn:un:unece:uncefact:documentation:2" xmlns:qdt="urn:oasis:names:specification:ubl:schema:xsd:QualifiedDatatypes-2" xmlns:udt="urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2" xmlns:default="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" exclude-result-prefixes="cac cbc ccts qdt udt default">
    <xsl:output method="xml" encoding="utf-8" indent="yes"/>
    
    <xsl:key name="PerAdditionalItemProperty" match="cac:InvoiceLine" use="cac:Item/cac:AdditionalItemProperty/cbc:Value"/>

    <xsl:template match="/">
        <PurchaseInvoices_version_1.0>
            <xsl:for-each select="default:Invoice">
                <PurchaseInvoice>
                    <xsl:for-each select="cac:InvoiceLine[generate-id(.)=generate-id(key('PerAdditionalItemProperty',cac:Item/cac:AdditionalItemProperty/cbc:Value)[1])]">
                        <xsl:for-each select="key('PerAdditionalItemProperty',cac:Item/cac:AdditionalItemProperty/cbc:Value)">
                            <xsl:if test="position()=1">
                                <Line>
                                    <Item><xsl:value-of select="cac:Item/cac:AdditionalItemProperty/cbc:Value"/></Item>
                                    <LineAmount><xsl:value-of select="format-number(sum(key('PerAdditionalItemProperty',cac:Item/cac:AdditionalItemProperty/cbc:Value)/cbc:LineExtensionAmount[number(.) = number(.)]),'#.##')"/></LineAmount>
                                </Line>
                            </xsl:if>
                        </xsl:for-each>
                    </xsl:for-each>
                </PurchaseInvoice>
            </xsl:for-each>
        </PurchaseInvoices_version_1.0>
    </xsl:template>
</xsl:stylesheet> 

The output that I expect is:

<?xml version="1.0" encoding="utf-8"?>
<PurchaseInvoices_version_1.0>
   <PurchaseInvoice>
      <Line>
         <Item>Kees Netelvrees</Item>
         <LineAmount>93.46</LineAmount>
      </Line>
      <Line>
         <Item>Jaap Aap</Item>
         <LineAmount>102.77</LineAmount>
      </Line>
   </PurchaseInvoice>
</PurchaseInvoices_version_1.0

Solution

  • You just need to put the conditions into the key expression:

    <xsl:stylesheet
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
        xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
        version="1.0">
      
      <xsl:output indent="yes"/>
      
      <xsl:key name="group" match="cac:InvoiceLine" use="cac:Item/cac:AdditionalItemProperty[cbc:Name = 'naamKandidaat']/cbc:Value"/>
    
    
      <xsl:template match="cac:InvoiceLine[generate-id() = generate-id(key('group', cac:Item/cac:AdditionalItemProperty[cbc:Name = 'naamKandidaat']/cbc:Value)[1])]">
        <Line>
          <Item>
            <xsl:value-of select="cac:Item/cac:AdditionalItemProperty[cbc:Name = 'naamKandidaat']/cbc:Value"/>
          </Item>
          <LineAmount>
            <xsl:value-of select="sum(key('group', cac:Item/cac:AdditionalItemProperty[cbc:Name = 'naamKandidaat']/cbc:Value)/cbc:LineExtensionAmount)"/>
          </LineAmount>
        </Line>
      </xsl:template>
      
      <xsl:template match="cac:InvoiceLine[not(generate-id() = generate-id(key('group', cac:Item/cac:AdditionalItemProperty[cbc:Name = 'naamKandidaat']/cbc:Value)[1]))]"/>
    
      
      <xsl:template match="/">
        <PurchaseInvoices_version_1.0>
          <PurchaseInvoice>
            <xsl:apply-templates/>
          </PurchaseInvoice>
        </PurchaseInvoices_version_1.0>
      </xsl:template>
    
    </xsl:stylesheet>