Search code examples
xmlxsltxml-parsingxslt-1.0xsl-grouping

xsl grouping of repetitive nodes by xml element in xslt1


I have a complex xml structure that looks like :

  <Items>
      <Item>
          <ItemTexts>
            <ItemText>
              <ItemTextsType>type1</ItemTextsType>
              <ItemTextsTypeDesc>description11</ItemTextsTypeDesc>
              <ItemTextsLine>1</ItemTextsLine>
            </ItemText>
            <ItemText>
              <ItemTextsType>type1</ItemTextsType>
              <ItemTextsTypeDesc>description12</ItemTextsTypeDesc>
              <ItemTextsLine>2</ItemTextsLine>
            </ItemText>
            <ItemText>
              <ItemTextsType>type2</ItemTextsType>
              <ItemTextsTypeDesc>description21</ItemTextsTypeDesc>
              <ItemTextsLine>3</ItemTextsLine>
            </ItemText>
            <ItemText>
              <ItemTextsType>type2</ItemTextsType>
              <ItemTextsTypeDesc>description22</ItemTextsTypeDesc>
              <ItemTextsLine>4</ItemTextsLine>
            </ItemText>
          </ItemTexts>
         </Item>
    <Item>
          <ItemTexts>
            <ItemText>
              <ItemTextsType>type1</ItemTextsType>
              <ItemTextsTypeDesc>description11</ItemTextsTypeDesc>
              <ItemTextsLine>1</ItemTextsLine>
            </ItemText>
            <ItemText>
              <ItemTextsType>type1</ItemTextsType>
              <ItemTextsTypeDesc>description12</ItemTextsTypeDesc>
              <ItemTextsLine>2</ItemTextsLine>
            </ItemText>
            <ItemText>
              <ItemTextsType>type2</ItemTextsType>
              <ItemTextsTypeDesc>description21</ItemTextsTypeDesc>
              <ItemTextsLine>3</ItemTextsLine>
            </ItemText>
            <ItemText>
              <ItemTextsType>type2</ItemTextsType>
              <ItemTextsTypeDesc>description22</ItemTextsTypeDesc>
              <ItemTextsLine>4</ItemTextsLine>
            </ItemText>
          </ItemTexts>
         </Item>
<Item>
          <ItemTexts>
            <ItemText>
              <ItemTextsType>type1</ItemTextsType>
              <ItemTextsTypeDesc>description11</ItemTextsTypeDesc>
              <ItemTextsLine>1</ItemTextsLine>
            </ItemText>
            <ItemText>
              <ItemTextsType>type1</ItemTextsType>
              <ItemTextsTypeDesc>description12</ItemTextsTypeDesc>
              <ItemTextsLine>2</ItemTextsLine>
            </ItemText>
            <ItemText>
              <ItemTextsType>type2</ItemTextsType>
              <ItemTextsTypeDesc>description21</ItemTextsTypeDesc>
              <ItemTextsLine>3</ItemTextsLine>
            </ItemText>
            <ItemText>
              <ItemTextsType>type2</ItemTextsType>
              <ItemTextsTypeDesc>description22</ItemTextsTypeDesc>
              <ItemTextsLine>4</ItemTextsLine>
            </ItemText>
          </ItemTexts>
         </Item>
    <Item>
          <ItemTexts>
            <ItemText>
              <ItemTextsType>type3</ItemTextsType>
              <ItemTextsTypeDesc>description31</ItemTextsTypeDesc>
              <ItemTextsLine>1</ItemTextsLine>
            </ItemText>
            <ItemText>
              <ItemTextsType>type3</ItemTextsType>
              <ItemTextsTypeDesc>description32</ItemTextsTypeDesc>
              <ItemTextsLine>2</ItemTextsLine>
            </ItemText>
            <ItemText>
              <ItemTextsType>type2</ItemTextsType>
              <ItemTextsTypeDesc>description21</ItemTextsTypeDesc>
              <ItemTextsLine>3</ItemTextsLine>
            </ItemText>
            <ItemText>
              <ItemTextsType>type2</ItemTextsType>
              <ItemTextsTypeDesc>description22</ItemTextsTypeDesc>
              <ItemTextsLine>4</ItemTextsLine>
            </ItemText>
          </ItemTexts>
         </Item>
    </Items>

I run with xsl on each Item like :

<xsl:for-each select="Items/Item">

I need an example of how to group <ItemText> by <ItemTextsType> individually for each <Item> so the outcome will be like :

For first <Item> in this example :

type1

description11

description12

type2

description21

description22

For second Item in this example :

type3

description31

description32

type2

description21

description22

of course I will arrange the outcome in a table like :

<table width="100%" border="1" style="display: block;">
        <tbody>
            <tr>
                <td id="SelectedRowLinkageContents">
                    <table width="100%" dir="ltr">
                        <tbody>
                            <tr style="background-color: #507CD1; text-align: center">
                                <td colspan="3" style="font: bold; color: white">
                                    Item1
                                </td>
                            </tr>
                            <tr>
                                <td style="height: 35px; font: bold; color: #507CD1;">
                                    type1
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    desription11
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    desription12
                                </td>
                            </tr>
                            <tr>
                                <td style="height: 35px; font: bold; color: #507CD1;">
                                    type2
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    desription21
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    desription22
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    desription23
                                </td>
                            </tr>
                        </tbody>
                    </table>

                    <table width="100%" dir="ltr">
                        <tbody>
                            <tr style="background-color: #507CD1; text-align: center">
                                <td colspan="3" style="font: bold; color: white">
                                    Item2
                                </td>
                            </tr>
                            <tr>
                                <td style="height: 35px; font: bold; color: #507CD1;">
                                    type1
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    desription11
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    desription12
                                </td>
                            </tr>
                            <tr>
                                <td style="height: 35px; font: bold; color: #507CD1;">
                                    type2
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    desription21
                                </td>
                            </tr>

                            <tr>
                                <td>
                                    desription23
                                </td>
                            </tr>
                        </tbody>
                    </table>

                    <table width="100%" dir="ltr">
                        <tbody>
                            <tr style="background-color: #507CD1; text-align: center">
                                <td colspan="3" style="font: bold; color: white">
                                    Item3
                                </td>
                            </tr>
                            <tr>
                                <td style="height: 35px; font: bold; color: #507CD1;">
                                    type1
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    desription11
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    desription12
                                </td>
                            </tr>
                            <tr>
                                <td style="height: 35px; font: bold; color: #507CD1;">
                                    type2
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    desription21
                                </td>
                            </tr>

                            <tr>
                                <td>
                                    desription23
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </td>
            </tr>
        </tbody>
    </table>

The point is that there is separate grouping for each <Item> into it's own <table> and inside that <table> there is groupping by <ItemTextsType>

I have tried something like

<xsl:key name="item-texts-type" match="ItemText" use="ItemTextsType" />


<xsl:for-each select="ItemTexts/ItemText[count(. | key('item-texts-type', ItemTextsType)[1]) = 1]">

                        <xsl:sort select="ItemTextsType" />
                        <tr>
                          <td style ="height:35px;font: bold; color:#507CD1;">
                            <xsl:value-of select="ItemTextsType" />
                          </td>
                        </tr>

                        <xsl:for-each select="key('item-texts-type', ItemTextsType)">
                          <tr>
                            <td>
                               <xsl:value-of select="ItemTextsTypeDesc" />
                            </td>
                          </tr>
                        </xsl:for-each>


                      </xsl:for-each>

but it only work on non repetitive nodes (if there were only one <Item>it would work fine). I can't change xml because it comes from customer .

Please help me , I need it ASAP .

Thank you !!!!


Solution

  • It's still difficult to tell what the desired solution is due to some discrepancies - for instance, your input XML has 4 <Item> elements, but your desired output only takes 3 of them into account. Additionally, several descriptions in your desired output do not match up with their expected location in the input XML document.

    That said, if we focus on your stated desired solution:

    "The point is that there is separate grouping for each <Item> into it's own <table> and inside that <table> there is groupping by <ItemTextsType>"

    ...then this ought to get you in the right direction.

    When this XSLT:

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
      <xsl:output omit-xml-declaration="yes" indent="yes" />
      <xsl:strip-space elements="*" />
    
      <xsl:key
        name="kItemTextByType"
        match="ItemText"
        use="concat(generate-id(ancestor::Item[1]), '+', ItemTextsType)" />
    
      <xsl:template match="node()|@*">
        <xsl:copy>
          <xsl:apply-templates select="node()|@*" />
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="/*">
        <table width="100%" border="1" style="display: block;">
          <tbody>
            <tr>
              <td id="SelectedRowLinkageContents">
                <xsl:apply-templates />
              </td>
            </tr>
          </tbody>
        </table>
      </xsl:template>
    
      <xsl:template match="Item">
        <table width="100%" dir="ltr">
          <tbody>
            <tr style="background-color: #507CD1; text-align: center">
              <td colspan="3" style="font: bold; color: white">
                <xsl:value-of select="concat('Item', position())" />
              </td>
            </tr>
            <xsl:apply-templates 
              select="ItemTexts/ItemText[
                generate-id() = 
                generate-id(
                  key(
                    'kItemTextByType', 
                     concat(generate-id(current()), '+', ItemTextsType)
                  )[1]
                )
              ]">
              <xsl:sort select="ItemTextsType" />
            </xsl:apply-templates>
          </tbody>
        </table>
      </xsl:template>
    
      <xsl:template match="ItemText">
        <tr>
          <td style="height: 35px; font: bold; color: #507CD1;">
            <xsl:value-of select="ItemTextsType" />
          </td>
        </tr>
        <xsl:for-each
          select="key(
            'kItemTextByType', 
            concat(
              generate-id(ancestor::Item[1]),
              '+',
              ItemTextsType
            )
          )"
        >
          <xsl:sort select="ItemTextsTypeDesc" />
          <tr>
            <td>
              <xsl:value-of select="ItemTextsTypeDesc" />
            </td>
          </tr>
        </xsl:for-each>
      </xsl:template>
    
    </xsl:stylesheet>
    

    ...is applied to the provided input XML:

    <?xml version="1.0" encoding="UTF-8"?>
    <Items>
      <Item>
        <ItemTexts>
          <ItemText>
            <ItemTextsType>type1</ItemTextsType>
            <ItemTextsTypeDesc>description11</ItemTextsTypeDesc>
            <ItemTextsLine>1</ItemTextsLine>
          </ItemText>
          <ItemText>
            <ItemTextsType>type1</ItemTextsType>
            <ItemTextsTypeDesc>description12</ItemTextsTypeDesc>
            <ItemTextsLine>2</ItemTextsLine>
          </ItemText>
          <ItemText>
            <ItemTextsType>type2</ItemTextsType>
            <ItemTextsTypeDesc>description21</ItemTextsTypeDesc>
            <ItemTextsLine>3</ItemTextsLine>
          </ItemText>
          <ItemText>
            <ItemTextsType>type2</ItemTextsType>
            <ItemTextsTypeDesc>description22</ItemTextsTypeDesc>
            <ItemTextsLine>4</ItemTextsLine>
          </ItemText>
        </ItemTexts>
      </Item>
      <Item>
        <ItemTexts>
          <ItemText>
            <ItemTextsType>type1</ItemTextsType>
            <ItemTextsTypeDesc>description11</ItemTextsTypeDesc>
            <ItemTextsLine>1</ItemTextsLine>
          </ItemText>
          <ItemText>
            <ItemTextsType>type1</ItemTextsType>
            <ItemTextsTypeDesc>description12</ItemTextsTypeDesc>
            <ItemTextsLine>2</ItemTextsLine>
          </ItemText>
          <ItemText>
            <ItemTextsType>type2</ItemTextsType>
            <ItemTextsTypeDesc>description21</ItemTextsTypeDesc>
            <ItemTextsLine>3</ItemTextsLine>
          </ItemText>
          <ItemText>
            <ItemTextsType>type2</ItemTextsType>
            <ItemTextsTypeDesc>description22</ItemTextsTypeDesc>
            <ItemTextsLine>4</ItemTextsLine>
          </ItemText>
        </ItemTexts>
      </Item>
      <Item>
        <ItemTexts>
          <ItemText>
            <ItemTextsType>type1</ItemTextsType>
            <ItemTextsTypeDesc>description11</ItemTextsTypeDesc>
            <ItemTextsLine>1</ItemTextsLine>
          </ItemText>
          <ItemText>
            <ItemTextsType>type1</ItemTextsType>
            <ItemTextsTypeDesc>description12</ItemTextsTypeDesc>
            <ItemTextsLine>2</ItemTextsLine>
          </ItemText>
          <ItemText>
            <ItemTextsType>type2</ItemTextsType>
            <ItemTextsTypeDesc>description21</ItemTextsTypeDesc>
            <ItemTextsLine>3</ItemTextsLine>
          </ItemText>
          <ItemText>
            <ItemTextsType>type2</ItemTextsType>
            <ItemTextsTypeDesc>description22</ItemTextsTypeDesc>
            <ItemTextsLine>4</ItemTextsLine>
          </ItemText>
        </ItemTexts>
      </Item>
      <Item>
        <ItemTexts>
          <ItemText>
            <ItemTextsType>type3</ItemTextsType>
            <ItemTextsTypeDesc>description31</ItemTextsTypeDesc>
            <ItemTextsLine>1</ItemTextsLine>
          </ItemText>
          <ItemText>
            <ItemTextsType>type3</ItemTextsType>
            <ItemTextsTypeDesc>description32</ItemTextsTypeDesc>
            <ItemTextsLine>2</ItemTextsLine>
          </ItemText>
          <ItemText>
            <ItemTextsType>type2</ItemTextsType>
            <ItemTextsTypeDesc>description21</ItemTextsTypeDesc>
            <ItemTextsLine>3</ItemTextsLine>
          </ItemText>
          <ItemText>
            <ItemTextsType>type2</ItemTextsType>
            <ItemTextsTypeDesc>description22</ItemTextsTypeDesc>
            <ItemTextsLine>4</ItemTextsLine>
          </ItemText>
        </ItemTexts>
      </Item>
    </Items>
    

    ...the wanted result is (I think) produced:

    <table width="100%" border="1" style="display: block;">
      <tbody>
        <tr>
          <td id="SelectedRowLinkageContents">
            <table width="100%" dir="ltr">
              <tbody>
                <tr style="background-color: #507CD1; text-align: center">
                  <td colspan="3" style="font: bold; color: white">Item1</td>
                </tr>
                <tr>
                  <td style="height: 35px; font: bold; color: #507CD1;">type1</td>
                </tr>
                <tr>
                  <td>description11</td>
                </tr>
                <tr>
                  <td>description12</td>
                </tr>
                <tr>
                  <td style="height: 35px; font: bold; color: #507CD1;">type2</td>
                </tr>
                <tr>
                  <td>description21</td>
                </tr>
                <tr>
                  <td>description22</td>
                </tr>
              </tbody>
            </table>
            <table width="100%" dir="ltr">
              <tbody>
                <tr style="background-color: #507CD1; text-align: center">
                  <td colspan="3" style="font: bold; color: white">Item2</td>
                </tr>
                <tr>
                  <td style="height: 35px; font: bold; color: #507CD1;">type1</td>
                </tr>
                <tr>
                  <td>description11</td>
                </tr>
                <tr>
                  <td>description12</td>
                </tr>
                <tr>
                  <td style="height: 35px; font: bold; color: #507CD1;">type2</td>
                </tr>
                <tr>
                  <td>description21</td>
                </tr>
                <tr>
                  <td>description22</td>
                </tr>
              </tbody>
            </table>
            <table width="100%" dir="ltr">
              <tbody>
                <tr style="background-color: #507CD1; text-align: center">
                  <td colspan="3" style="font: bold; color: white">Item3</td>
                </tr>
                <tr>
                  <td style="height: 35px; font: bold; color: #507CD1;">type1</td>
                </tr>
                <tr>
                  <td>description11</td>
                </tr>
                <tr>
                  <td>description12</td>
                </tr>
                <tr>
                  <td style="height: 35px; font: bold; color: #507CD1;">type2</td>
                </tr>
                <tr>
                  <td>description21</td>
                </tr>
                <tr>
                  <td>description22</td>
                </tr>
              </tbody>
            </table>
            <table width="100%" dir="ltr">
              <tbody>
                <tr style="background-color: #507CD1; text-align: center">
                  <td colspan="3" style="font: bold; color: white">Item4</td>
                </tr>
                <tr>
                  <td style="height: 35px; font: bold; color: #507CD1;">type2</td>
                </tr>
                <tr>
                  <td>description21</td>
                </tr>
                <tr>
                  <td>description22</td>
                </tr>
                <tr>
                  <td style="height: 35px; font: bold; color: #507CD1;">
                  type3</td>
                </tr>
                <tr>
                  <td>description31</td>
                </tr>
                <tr>
                  <td>description32</td>
                </tr>
              </tbody>
            </table>
          </td>
        </tr>
      </tbody>
    </table>
    

    ...which, when rendered in an HTML page, looks like this:

    HTML table