Vp Soini
Vp Soini

Reputation: 83

XSLT / link elements to group

Yet another XSLT/Grouping question, but I could not find the answer for this twist. I know how to do Muenichian grouping, but I'm troubled with trying to link sub-element content belonging to any of the grouped elements in that group, where that parent is grouped to.

The idea here is that elements are grouped by content-color combination, so that would generate 4 different combinations with this sample file. For those groups, prices are calculated together within the group. Some of the goodslines includes some extras, which then would need to be mapped into correct group and that's where the problem lies...

Those extra lines need to be read in separately as "zero-lines" and they should have a number, which links to the "group number" generated by the XSL

I have tried to create preceding-sibling to match that group but I only get it work into original positions, not grouped ones

XML-sample

<shipment>
    <goodslines>
        <goodsline>
            <content>content1</content>
            <color>color1</color>
            <price>10</price>
            <extras>
                <extra>extra-a</extra>
                <price>5</price>
            </extras>
        </goodsline>
        <goodsline>
            <content>content1</content>
            <color>color1</color>
            <price>10</price>
        </goodsline>
        <goodsline>
            <content>content2</content>
            <color>color1</color>
            <price>20</price>
            <extras>
                <extra>extra-b</extra>
                <price>15</price>
            </extras>
        </goodsline>
        <goodsline>
            <content>content2</content>
            <color>color1</color>
            <price>20</price>
            <extras>
                <extra>extra-c</extra>
                <price>25</price>
            </extras>
        </goodsline>
        <goodsline>
            <content>content2</content>
            <color>color2</color>
            <price>25</price>
        </goodsline>
        <goodsline>
            <content>content3</content>
            <color>color1</color>
            <price>30</price>
        </goodsline>
        <goodsline>
            <content>content3</content>
            <color>color1</color>
            <price>30</price>
        </goodsline>
    </goodslines>
</shipment>

XSL (1.0):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
    <xsl:output method="text" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:template match="/">
        <xsl:call-template name="Package"/>
    </xsl:template>
    <xsl:key name="GoodsLine" match="//goodsline" use="concat(content, '|', color)"/>
    <xsl:key name="Extra" match="//extras" use="extra"/>
    <xsl:template name="Package">
        <xsl:for-each select="/shipment/goodslines/goodsline[generate-id()=generate-id(key('GoodsLine',concat(content, '|', color)))]">
            <xsl:value-of select="position()" />
            <xsl:text>§</xsl:text>
            <xsl:value-of select="content"/>
            <xsl:text>§</xsl:text>
            <xsl:value-of select="sum(key('GoodsLine', concat(content, '|', color))/price)"/>
            <xsl:text>&#xD;</xsl:text>
        </xsl:for-each>

        <xsl:for-each select="/shipment/goodslines/goodsline/extras">
            <xsl:text>0§</xsl:text>
            <xsl:value-of select="../content"/>
            <xsl:text>§</xsl:text>
            <xsl:value-of select="price"/>
            <xsl:text>§</xsl:text>
            <xsl:value-of select="extra"/>
            <xsl:text>§</xsl:text>
            <xsl:value-of select="count(../preceding-sibling::goodsline[generate-id()=generate-id(key('GoodsLine',concat(content, '|', color)))])+1 "/>
            <xsl:text>&#xD;</xsl:text>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

Desired output would be:

1§content1§20
2§content2§40
3§content2§25
4§content3§60
0§content1§5§extra-a§1
0§content2§15§extra-b§2
0§content2§25§extra-c§2

where the format is:

GroupNo§Content§GroupPriceSum§

and for extras:

Zero§Content§ExtraPrice§ExtraName§GroupBelongTo

So that last number of those extra-lines indicates to which group that belongs to.

Now I get:

1§content1§20
2§content2§40
3§content2§25
4§content3§60
0§content1§5§extra-a§1
0§content2§15§extra-b§2
0§content2§25§extra-c§3

Where the last one goes wrong. The last line should belong to "content2/color1" group like that extra-b. By trying to move those extra lines between different goodsline-elements and/or adding them will cause result to fail more or less. There's something wrong in my prceding-sibling thinking, but what it is?

And yes, I can only use XSLT 1.0

Upvotes: 0

Views: 39

Answers (1)

michael.hor257k
michael.hor257k

Reputation: 117175

I would suggest you try it this way:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>

<xsl:key name="line" match="goodsline" use="concat(content, '|', color)"/>

<xsl:template match="/shipment">
    <!-- distinct elements -->
    <xsl:variable name="groups" select="goodslines/goodsline[generate-id()=generate-id(key('line',concat(content, '|', color)))]"/>
    <!-- goods -->
    <xsl:for-each select="$groups">
        <xsl:value-of select="position()" />
        <xsl:text>§</xsl:text>
        <xsl:value-of select="content"/>
        <xsl:text>§</xsl:text>
        <xsl:value-of select="sum(key('line', concat(content, '|', color))/price)"/>
        <xsl:text>&#xD;</xsl:text>
    </xsl:for-each>
    <!-- extras -->
    <xsl:for-each select="$groups">
        <xsl:variable name="group-number" select="position()" />
        <!-- extras in this group -->
        <xsl:for-each select="key('line', concat(content, '|', color))/extras">
            <xsl:text>0§</xsl:text>
            <xsl:value-of select="../content"/>
            <xsl:text>§</xsl:text>
            <xsl:value-of select="price"/>
            <xsl:text>§</xsl:text>
            <xsl:value-of select="extra"/>
            <xsl:text>§</xsl:text>
            <xsl:value-of select="$group-number"/>
            <xsl:text>&#xD;</xsl:text>
        </xsl:for-each>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

This saves the distinct groups to a variable, then processes the variable twice - once for the groups themselves, and once for their extras. This way you get the same group number on each pass.

Upvotes: 1

Related Questions