AbRe
AbRe

Reputation: 162

Group XML file based on a given number using XSLT

I want to group a XML file based on a XML tag value. Below is my input XML.

<Root>
    <GroupNumber>3</GroupNumber>
    <EDICustomer>
        <Company>IWS</Company>
        <Customer>150</Customer>
        <CreationDate>2020-03-15T10:29:08.813</CreationDate>
        <CreationTime>2020-03-15T10:29:08.813</CreationTime>
        <EDICustomerLine>
            <Barcode>447896130245</Barcode>
            <ItemDescription>Medium</ItemDescription>
            <Warehouse>B01</Warehouse>
            <Status>12</Status>
        </EDICustomerLine>
        <EDICustomerLine>
            <Barcode>447896130245</Barcode>
            <ItemDescription>Medium</ItemDescription>
            <Warehouse>B01</Warehouse>
            <Status>12</Status>
        </EDICustomerLine>
        <EDICustomerLine>
            <Barcode>447896130245</Barcode>
            <ItemDescription>Medium</ItemDescription>
            <Warehouse>B01</Warehouse>
            <Status>12</Status>
        </EDICustomerLine>
        <EDICustomerLine>
            <Barcode>447896130245</Barcode>
            <ItemDescription>Medium</ItemDescription>
            <Warehouse>B01</Warehouse>
            <Status>12</Status>
        </EDICustomerLine>
    </EDICustomer>
</Root>

I don't know how many EDICustomerLine tags will be there. If GroupNumber value is greater than the number of EDICustomerLine tags leave the EDICustomer child tags as it is but if the GroupNumber is less than EDICustomerLine tags we have to split the EDICustomerLine tags based on the GroupNumber tag value. So in this scenario the expected XML will be like following.

<Root>
    <EDICustomer>
        <Company>IWS</Company>
        <Customer>150</Customer>
        <CreationDate>2020-03-15T10:29:08.813</CreationDate>
        <CreationTime>2020-03-15T10:29:08.813</CreationTime>
        <EDICustomerLine>
            <Barcode>447896130245</Barcode>
            <ItemDescription>Medium</ItemDescription>
            <Warehouse>B01</Warehouse>
            <Status>12</Status>
        </EDICustomerLine>
        <EDICustomerLine>
            <Barcode>447896130245</Barcode>
            <ItemDescription>Medium</ItemDescription>
            <Warehouse>B01</Warehouse>
            <Status>12</Status>
        </EDICustomerLine>
        <EDICustomerLine>
            <Barcode>447896130245</Barcode>
            <ItemDescription>Medium</ItemDescription>
            <Warehouse>B01</Warehouse>
            <Status>12</Status>
        </EDICustomerLine>
    </EDICustomer>
    <EDICustomer>
        <Company>IWS</Company>
        <Customer>150</Customer>
        <CreationDate>2020-03-15T10:29:08.813</CreationDate>
        <CreationTime>2020-03-15T10:29:08.813</CreationTime>
        <EDICustomerLine>
            <Barcode>447896130245</Barcode>
            <ItemDescription>Medium</ItemDescription>
            <Warehouse>B01</Warehouse>
            <Status>12</Status>
        </EDICustomerLine>
    </EDICustomer>
</Root>

First EDICustomer parent tag contains Company, Customer, CreationDate, CreationTime and 3 based on the Group number value. Like that 2nd EDICustomer should contain 3 or less tags. We don't know how many EDICustomerLine tags and the value of Group Number will be there. Also

<Company>IWS</Company>
        <Customer>150</Customer>
        <CreationDate>2020-03-15T10:29:08.813</CreationDate>
        <CreationTime>2020-03-15T10:29:08.813</CreationTime>

This above part should repeat in all the EDICustomer groups (This part will be possible if we find a way to loop EDICustomerLine based on group number).

First of all I want to know whether this scenario is possible in XSLT.
If possible, can you guys help me?

Upvotes: 1

Views: 181

Answers (1)

zx485
zx485

Reputation: 29042

You can achieve the desired result with the following XSLT-2.0 stylesheet. It groups the <EDICustomerLine> elements in groups of GroupNumber and then iterates over the current-group(). The constant part is created by the first xsl:copy-of.

<?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" omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="text()" />

  <!-- identity template -->
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*" />
    </xsl:copy>
   </xsl:template>  

   <xsl:template match="Root">
     <xsl:variable name="max" select="GroupNumber" />
     <xsl:copy>
        <!-- Handle the "zero-items" situation" -->
        <xsl:if test="count(EDICustomer/EDICustomerLine) = 0">
            <EDICustomer>
                <xsl:copy-of select="EDICustomer[1]/Company | EDICustomer[1]/Customer | EDICustomer[1]/CreationDate | EDICustomer[1]/CreationTime" />
            </EDICustomer>
        </xsl:if>
        <xsl:for-each-group select="EDICustomer/EDICustomerLine" group-by="(position()-1) idiv $max">
            <EDICustomer>
                <!-- Add a <GroupNr> element with the index of the group -->
                <GroupNr><xsl:value-of select="position()" /></GroupNr>
                <xsl:copy-of select="../Company | ../Customer | ../CreationDate | ../CreationTime" />
                <xsl:copy-of select="current-group()" />
            </EDICustomer>
        </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Its result is:

<Root>
    <EDICustomer>
        <GroupNr>1</GroupNr>
        <Company>IWS</Company>
        <Customer>150</Customer>
        <CreationDate>2020-03-15T10:29:08.813</CreationDate>
        <CreationTime>2020-03-15T10:29:08.813</CreationTime>
        <EDICustomerLine>
            <Barcode>447896130245</Barcode>
            <ItemDescription>Medium</ItemDescription>
            <Warehouse>B01</Warehouse>
            <Status>12</Status>
        </EDICustomerLine>
        <EDICustomerLine>
            <Barcode>447896130245</Barcode>
            <ItemDescription>Medium</ItemDescription>
            <Warehouse>B01</Warehouse>
            <Status>12</Status>
        </EDICustomerLine>
        <EDICustomerLine>
            <Barcode>447896130245</Barcode>
            <ItemDescription>Medium</ItemDescription>
            <Warehouse>B01</Warehouse>
            <Status>12</Status>
        </EDICustomerLine>
    </EDICustomer>
    <EDICustomer>
        <GroupNr>2</GroupNr>
        <Company>IWS</Company>
        <Customer>150</Customer>
        <CreationDate>2020-03-15T10:29:08.813</CreationDate>
        <CreationTime>2020-03-15T10:29:08.813</CreationTime>
        <EDICustomerLine>
            <Barcode>447896130245</Barcode>
            <ItemDescription>Medium</ItemDescription>
            <Warehouse>B01</Warehouse>
            <Status>12</Status>
        </EDICustomerLine>
    </EDICustomer>
</Root>

This is as desired.

Upvotes: 1

Related Questions