Warm tip: This article is reproduced from serverfault.com, please click

XSLT 1.0 Grouping xml nodes by value of a child

发布于 2020-12-02 12:47:36

I am using XSLT 1.0

I have an input message i am trying to transform. This input messages has the following redondant structure, composed of 4 types of nodes A, B, Order, Article, which are always in the same order:

A
B
Order0
Article100
A
B
Order1
Article101
A
B
Order0
Article102
...

I would like to group the nodes to produce the following output:

A
B
Order0
Article100
Article102
A
B
Order1
Article101
A
B

In the output, i don't want any duplicates of the Order node; The nodes A and B should not be moved.

Here are complete XML messages. The key for the grouping is the field Key inside of the Order node

Input:

<Messages>
    <Message>
        <A>
            <Number>100</Number>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </A>
    </Message>
    <Message>
        <B>
            <Number>100</Number>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </B>
    </Message>
    <Message>
        <Order>
            <Key>100</Key>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </Order>
    </Message>
    <Message>
        <Article>
            <Key>78</Key>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </Article>
    </Message>
    <Message>
        <A>
            <Number>600</Number>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </A>
    </Message>
    <Message>
        <B>
            <Number>601</Number>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </B>
    </Message>
    <Message>
        <Order>
            <Key>101</Key>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </Order>
    </Message>
    <Message>
        <Article>
            <Key>55</Key>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </Article>
    </Message>
    <Message>
        <A>
            <Number>799</Number>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </A>
    </Message>
    <Message>
        <B>
            <Number>798</Number>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </B>
    </Message>
    <Message>
        <Order>
            <Key>100</Key>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </Order>
    </Message>
    <Message>
        <Article>
            <Key>32</Key>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </Article>
    </Message>
</Messages>

Output:

<Messages>
    <Message>
        <A>
            <Number>100</Number>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </A>
    </Message>
    <Message>
        <B>
            <Number>100</Number>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </B>
    </Message>
    <Message>
        <Order>
            <Key>100</Key>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </Order>
    </Message>
    <Message>
        <Article>
            <Key>78</Key>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </Article>
    </Message>
    <Message>
        <Article>
            <Key>32</Key>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </Article>
    </Message>
    <Message>
        <A>
            <Number>600</Number>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </A>
    </Message>
    <Message>
        <B>
            <Number>601</Number>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </B>
    </Message>
    <Message>
        <Order>
            <Key>101</Key>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </Order>
    </Message>
    <Message>
        <Article>
            <Key>55</Key>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </Article>
    </Message>
    <Message>
        <A>
            <Number>799</Number>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </A>
    </Message>
    <Message>
        <B>
            <Number>798</Number>
            <SomeData>somedata</SomeData>
            <SomeData2>somedata</SomeData2>
        </B>
    </Message>
</Messages>

I was thinking about using the Muenchian grouping but without success. I am not quite sure it is what I am looking for.

This is for a Biztalk mapping, so I could also work with Biztalk mapping and functoids.

Questioner
JohanB
Viewed
0
michael.hor257k 2020-12-02 22:13:04

Here's one way you could look at it (I think):

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:strip-space elements="*"/>

<xsl:key name="order" match="Message[Order]" use="Order/Key" />

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

<xsl:template match="Message[Order]">
    <xsl:if test="count(. | key('order', Order/Key)[1]) = 1">
        <xsl:copy-of select="."/>
        <xsl:copy-of select="key('order', Order/Key)/following-sibling::Message[1]"/>
    </xsl:if>
</xsl:template>

<xsl:template match="Message[Article]"/>

</xsl:stylesheet>