Sabapathy
Sabapathy

Reputation: 63

XSLT 1.0 - Grouping xml elements

Need to transform the request from system A to fint into the request of system B.

Suppose I have an XML document that looks like this from system A:

<root>
<Bundle>
    <authors>
        <author>
            <authorID>100</authorID>
            <authorName>Kathisiera</authorName>
        </author>
        <author>
            <authorID>200</authorID>
            <authorName>Bates</authorName>
        </author>
        <author>
            <authorID>300</authorID>
            <authorName>Gavin King</authorName>
        </author>
    </authors>
    <books>
        <book>
            <bookOrderID>1111</bookOrderID>
            <bookName>Head First Java</bookName>
            <bookRefID>100</bookRefID>
        </book>
        <book>
            <bookOrderID>5555</bookOrderID>
            <bookName>Head First Servlets</bookName>
            <bookRefID>200</bookRefID>
        </book>
        <book>
            <bookOrderID>1111</bookOrderID>
            <bookName>Hibernate In Action</bookName>
            <bookRefID>300</bookRefID>
        </book>
    </books>
</Bundle>

I have to fit this request into the request structure of system B:

<root>
<Bundle>
    <authors>
        <author>
            <authorID>100</authorID>
            <authorName>Kathisiera</authorName>
        </author>
        <author>
            <authorID>300</authorID>
            <authorName>Gavin King</authorName>
        </author>
    </authors>
    <books>
        <book>
            <bookOrderID>1111</bookOrderID>
            <bookName>Head First Java</bookName>
            <bookRefID>100</bookRefID>
        </book>
        <book>
            <bookOrderID>1111</bookOrderID>
            <bookName>Hibernate In Action</bookName>
            <bookRefID>300</bookRefID>
        </book>
    </books>
</Bundle>
<Bundle>
    <authors>
        <author>
            <authorID>200</authorID>
            <authorName>Bates</authorName>
        </author>
    </authors>
    <books>
        <book>
            <bookOrderID>5555</bookOrderID>
            <bookName>Head First Servlets</bookName>
            <bookRefID>200</bookRefID>
        </book>
    </books>
</Bundle>

First I have to group book inside Bundle based on bookOrderID. Then group author inside Bundle by comparing bookRefID with authorID.

I tried usingkey() generate-id() function of xslt. But unable to get the expected result.

Please help me out to get the solution.

Upvotes: 3

Views: 148

Answers (2)

Tomalak
Tomalak

Reputation: 338376

Here is one way to do it:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>

  <xsl:key name="kBook" match="book" use="bookOrderID" />

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

  <xsl:template match="root">
    <xsl:apply-templates mode="bundle" select="Bundle/books/book[
      generate-id()
      =
      generate-id(key('kBook', bookOrderID)[1])
    ]" />
  </xsl:template>

  <xsl:template match="book" mode="bundle">
    <xsl:variable name="bookGroup" select="key('kBook', bookOrderID)" />
    <Bundle>
      <authors>
        <xsl:copy-of select="//author[authorID = $bookGroup/bookRefID]" />
      </authors>
      <books>
        <xsl:copy-of select="$bookGroup" />
      </books>
    </Bundle>
  </xsl:template>
</xsl:stylesheet>

This groups books by their bookOrderID using an <xsl:key>.

After that it uses a property of the = operator to find all related autors: The = operator compares all nodes on either side with each other. Think of it as a sort of "inner join" for node sets. This way you can copy the right nodes with a simple <xsl:copy-of>.

Upvotes: 0

Kirill Polishchuk
Kirill Polishchuk

Reputation: 56212

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes"/>

    <xsl:key name="k" match="book" use="bookOrderID"/>
    <xsl:key name="a" match="author" use="authorID"/>

    <xsl:template match="/root">
        <xsl:copy>
            <xsl:apply-templates select="//books"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="books">

        <xsl:apply-templates select="book[generate-id(.) = generate-id(key('k', bookOrderID))]"/>

    </xsl:template>

    <xsl:template match="book">
        <Bundle>
            <authors>
                <xsl:apply-templates select="key('a', key('k', bookOrderID)/bookRefID)"/>
            </authors>

            <books>
                <xsl:copy-of select="key('k', bookOrderID)"/>
            </books>
        </Bundle>
    </xsl:template>

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

</xsl:stylesheet>

Input:

<root>
    <Bundle>
        <authors>
            <author>
                <authorID>100</authorID>
                <authorName>Kathisiera</authorName>
            </author>
            <author>
                <authorID>200</authorID>
                <authorName>Bates</authorName>
            </author>
            <author>
                <authorID>300</authorID>
                <authorName>Gavin King</authorName>
            </author>
        </authors>
        <books>
            <book>
                <bookOrderID>1111</bookOrderID>
                <bookName>Head First Java</bookName>
                <bookRefID>100</bookRefID>
            </book>
            <book>
                <bookOrderID>5555</bookOrderID>
                <bookName>Head First Servlets</bookName>
                <bookRefID>200</bookRefID>
            </book>
            <book>
                <bookOrderID>1111</bookOrderID>
                <bookName>Hibernate In Action</bookName>
                <bookRefID>300</bookRefID>
            </book>
        </books>
    </Bundle>
</root>

Output:

<root>
    <Bundle>
        <authors>
            <author>
                <authorID>100</authorID>
                <authorName>Kathisiera</authorName>
            </author>
            <author>
                <authorID>300</authorID>
                <authorName>Gavin King</authorName>
            </author>
        </authors>
        <books>
            <book>
                <bookOrderID>1111</bookOrderID>
                <bookName>Head First Java</bookName>
                <bookRefID>100</bookRefID>
            </book>
            <book>
                <bookOrderID>1111</bookOrderID>
                <bookName>Hibernate In Action</bookName>
                <bookRefID>300</bookRefID>
            </book>
        </books>
    </Bundle>
    <Bundle>
        <authors>
            <author>
                <authorID>200</authorID>
                <authorName>Bates</authorName>
            </author>
        </authors>
        <books>
            <book>
                <bookOrderID>5555</bookOrderID>
                <bookName>Head First Servlets</bookName>
                <bookRefID>200</bookRefID>
            </book>
        </books>
    </Bundle>
</root>

Upvotes: 1

Related Questions