Search code examples
xslt-1.0xslt-grouping

How to traverse and sum XML document with many-to-many relationship using XSLT 1.0?


I need help to process following XML document using XSL 1.0, and I don't have the words to describe what the actual problem is.

I have a collection of items collectionA I'd like to process. collectionA is related to collectionB from which I'd like to get the sum of all related num values. The relationship is a many-to-many relationship, defined by the collectionC items, that connects the A and B items.

In any other object model based programming language I would use a loop. Problem is that I don't see how to both loop AND to collect the sum of all values in xslt 1.0...?

<root>
 <collectionA>
   <id>1</id>
 </collectionA>
 <collectionA>
  <id>2</id>
 </collectionA>

 <collectionB>
  <id>1</id>
  <num>11</num>
 </collectionB>
 <collectionB>
  <id>2</id>
  <num>22</num>
 </collectionB>
 <collectionB>
  <id>3</id>
  <num>33</num>
 </collectionB>
 <collectionB>
  <id>4</id>
  <num>44</num>
 </collectionB>

 <collectionC>
  <collectionAid>1</collectionAid>
  <collectionBid>1</collectionBid>
  <collectionBid>2</collectionBid>
 </collectionC>
 <collectionC>
  <collectionAid>2</collectionAid>
  <collectionBid>3</collectionBid>
  <collectionBid>4</collectionBid>
 </collectionC>
</root>

Expected result is:

<root>
 <collectionA>
  <id>1</id>
  <sum>33</sum>
 </collectionA>
 <collectionA>
  <id>2</id>
  <sum>77</sum>
 </collectionA>
</root>

Anyone!?


Solution

  • I don't see how to both loop AND to collect the sum of all values

    You do not need to do either one of these. All you need is a pair of keys to resolve the cross-references. Then use them to select the related nodes and sum their values:

    XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:key name="b" match="collectionB" use="id" />
    <xsl:key name="c" match="collectionC" use="collectionAid" />
    
    <xsl:template match="/root">
        <xsl:copy>
            <xsl:for-each select="collectionA">
                <xsl:copy>
                    <xsl:copy-of select="id"/>
                    <sum>
                        <xsl:value-of select="sum(key('b', key('c', id)/collectionBid)/num)"/>
                    </sum>
                </xsl:copy>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>