Search code examples
xslt-2.0xslt-grouping

XSLT 2, Mutiple grouping by attributes and elements


I can't seem to manage grouping couple based on their MaritalStatus.
I've manage to group all the person that are in a relationship ('DeFacto', 'Married') but can't figuring out how to group them in different households.

Basically if two persons make a couple they will create a Household. The household name will belong to the first person surname in the household, the NumberOfAdults will be hardcoded to '2' and the NumberOfDependants will be summed.

The couple is linked based on the Party.Identifier and the Party.MaritalStatus.RelatedEntityRef

What i have(simplified):

<PartySegment>
<Party Type="Guarantor" PrimaryApplicant="No">
    <Identifier>b8b0f908b08e</Identifier>
    <Person Sex="Female" FirstHomeBuyer="No" CustomerOfLender="No">
        <PersonName>
            <NameTitle Value="Lady"/>
            <FirstName>Clemansa</FirstName>
            <Surname>Sanchez</Surname>
        </PersonName>
        <MaritalStatus Status="DeFacto">
            <RelatedEntityRef>ea384b0bf3f5</RelatedEntityRef>
        </MaritalStatus>
        <NumberOfDependents>1</NumberOfDependents>
    </Person>
</Party>
<Party Type="Applicant" PrimaryApplicant="Yes" ExistingCustomerID="1231">
    <Identifier>bd8c65a3ad80</Identifier>
    <Person Sex="Female" FirstHomeBuyer="Yes" CustomerOfLender="Yes">
        <PersonName>
            <NameTitle Value="Mrs"/>
            <FirstName>Cheryl</FirstName>
            <Surname>Bonkers</Surname>
        </PersonName>
        <MaritalStatus Status="Married">
            <RelatedEntityRef>ee84dc9e38ec</RelatedEntityRef>
        </MaritalStatus>
        <NumberOfDependents>2</NumberOfDependents>
    </Person>
</Party>
<Party Type="Guarantor" PrimaryApplicant="No">
    <Identifier>ea384b0bf3f5</Identifier>
    <Person Sex="Male" FirstHomeBuyer="No" CustomerOfLender="No">
        <PersonName>
            <NameTitle Value="Mr"/>
            <FirstName>Greg</FirstName>
            <OtherName>Morty</OtherName>
            <Surname>Sanchez</Surname>
        </PersonName>
        <MaritalStatus Status="DeFacto">
            <RelatedEntityRef>b8b0f908b08e</RelatedEntityRef>
        </MaritalStatus>
        <NumberOfDependents>0</NumberOfDependents>
    </Person>
</Party>
<Party Type="Applicant" PrimaryApplicant="No">
    <Identifier>ee84dc9e38ec</Identifier>
    <Person Sex="Male" FirstHomeBuyer="No" CustomerOfLender="No">
        <PersonName>
            <NameTitle Value="Mr"/>
            <FirstName>Mark</FirstName>
            <Surname>Bonkers</Surname>
        </PersonName>
        <MaritalStatus Status="Married">
            <RelatedEntityRef>bd8c65a3ad80</RelatedEntityRef>
        </MaritalStatus>
        <NumberOfDependents>0</NumberOfDependents>
    </Person>
</Party>
</PartySegment>

The desire output:

<Household UniqueID="b8b0f908b08e-Household"
                Name="Sanchez Household"
                NumberOfAdults="2"
                NumberOfDependants="1"/>

<Household UniqueID="bd8c65a3ad80-Household"
                Name="Bonkers Household"
                NumberOfAdults="2"
                NumberOfDependants="2"/>

What i have so far(simplified): This is working for one couple because is not taking in consideration the MaritalStatus.RelatedEntityRef information

<xsl:template match="PartySegment" mode="Household_Couple">

    <xsl:for-each-group select="Party[Person/MaritalStatus/@Status = ('DeFacto', 'Married')]" group-by="Person/MaritalStatus/@Status = ('DeFacto', 'Married')">
        <xsl:variable name="owner_id" select="Identifier"/>

        <Household UniqueID="{concat(Identifier, '-Household')}" 
            Name="{normalize-space(concat(Person/PersonName/Surname, ' Household'))}"
            NumberOfAdults="{'2'}"
            NumberOfDependants="{if(Person/Dependent) then count(current-group()/Person/Dependent) else if(Person/NumberOfDependents) then sum(current-group()/Person/NumberOfDependents) else '0'}">

            <xsl:apply-templates select="current-group()/Person/Dependent"/>

        </Household>
    </xsl:for-each-group>
</xsl:template>

Solution

  • If you can move to XSLT 3.0 then I think a composite grouping key sort((Identifier, key('ref', Person/MaritalStatus/RelatedEntityRef)/Identifier)) of the sort of the two related identifiers can solve this:

    <xsl:key name="ref" match="Party" use="Identifier"/>
    
    <xsl:template match="PartySegment">
        <xsl:for-each-group select="Party[Person/MaritalStatus/@Status = ('DeFacto', 'Married')]" 
            composite="yes" 
            group-by="sort((Identifier, key('ref', Person/MaritalStatus/RelatedEntityRef)/Identifier))">
            <Household UniqueID="{concat(Identifier, '-Household')}" 
                Name="{normalize-space(concat(Person/PersonName/Surname, ' Household'))}"
                NumberOfAdults="{'2'}"
                NumberOfDependants="{if(Person/Dependent) then count(current-group()/Person/Dependent) else if(Person/NumberOfDependents) then sum(current-group()/Person/NumberOfDependents) else '0'}">
    
    
    
            </Household>
        </xsl:for-each-group>
    </xsl:template>
    

    I get

    <Household UniqueID="b8b0f908b08e-Household"
               Name="Sanchez Household"
               NumberOfAdults="2"
               NumberOfDependants="1"/>
    <Household UniqueID="bd8c65a3ad80-Household"
               Name="Bonkers Household"
               NumberOfAdults="2"
               NumberOfDependants="2"/>
    

    this way with Saxon 9.8 EE inside of oXygen.

    For Saxon 9.8 HE it should be possible to rewrite that as

    <xsl:key name="ref" match="Party" use="Identifier"/>
    
    <xsl:template match="PartySegment">
        <xsl:for-each-group select="Party[Person/MaritalStatus/@Status = ('DeFacto', 'Married')]" 
            composite="yes" 
            group-by="Identifier | key('ref', Person/MaritalStatus/RelatedEntityRef)/Identifier">
            <Household UniqueID="{concat(Identifier, '-Household')}" 
                Name="{normalize-space(concat(Person/PersonName/Surname, ' Household'))}"
                NumberOfAdults="{'2'}"
                NumberOfDependants="{if(Person/Dependent) then count(current-group()/Person/Dependent) else if(Person/NumberOfDependents) then sum(current-group()/Person/NumberOfDependents) else '0'}">
    
    
    
            </Household>
        </xsl:for-each-group>
    </xsl:template>
    

    For XSLT 2.0 where we don't have composite keys we would need to make sure to construct a single key with both components:

    <xsl:template match="PartySegment">
        <xsl:for-each-group select="Party[Person/MaritalStatus/@Status = ('DeFacto', 'Married')]" 
            group-by="string-join((Identifier | key('ref', Person/MaritalStatus/RelatedEntityRef)/Identifier), '|')">
            <Household UniqueID="{concat(Identifier, '-Household')}" 
                Name="{normalize-space(concat(Person/PersonName/Surname, ' Household'))}"
                NumberOfAdults="{'2'}"
                NumberOfDependants="{if(Person/Dependent) then count(current-group()/Person/Dependent) else if(Person/NumberOfDependents) then sum(current-group()/Person/NumberOfDependents) else '0'}">
    
    
    
            </Household>
        </xsl:for-each-group>
    </xsl:template>