0

I'm trying to format my xml data into two HTML tables. I successfully can sort some dummy data xls:sort, but I can't split up the sorted data into different tables.

My xml:

<a>
    <b id="N">text1</b>
    <b id="N">text2</b>
    <b id="N+1">text3</b>
    <b id="N">text4</b>
    <b id="N+2">text5</b>
    <b id="N+3">text6</b>
    <b id="N">text7</b>
    <b id="N+2">text8</b>
</a>

N is in this case a number, but I don't know which number. It could be 2 and 55, 3 and 4, 44 and 52 and 78 and 98.

Each number I wish to send to their own table, so the result would be:

<table>
    <tr><td>text1</td></tr>
    <tr><td>text2</td></tr>
    <tr><td>text4</td></tr>
    <tr><td>text7</td></tr>
</table>

<table>
    <tr><td>text3</td></tr>
</table>

<table>
    <tr><td>text5</td></tr>
    <tr><td>text8</td></tr>
</table>

<table>
    <tr><td>text6</td></tr>
</table>

How can I devide the sorted data into different tables depending on their attribute?

Any pointers would be appreciated.

z--
  • 2,186
  • 17
  • 33

1 Answers1

2

The standard approach to this kind of problem in XSLT 1.0 is called Muenchian grouping. You define a key that groups your target elements in the way you want

<xsl:key name="bsById" match="b" use="@id" />

then use a trick with generate-id to extract just the first node in each group as a proxy for the group as a whole

<xsl:apply-templates select="b[generate-id()
                             = generate-id(key('bsById', @id)[1])]"
                     mode="group">
  <xsl:sort select="@id" />
</xsl:apply-templates>

So now the following template would fire once per group, and you can use the key function within it to get all the nodes in the group

<xsl:template match="b" mode="group">
  <table>
    <!-- extract all the nodes that are grouped with this one -->
    <xsl:apply-templates select="key('bsById', @id)">
      <!-- you could <xsl:sort> here if you want to sort within groups -->
    </xsl:apply-templates>
  </table>
</xsl:template>

<xsl:template match="b">
  <tr><td>...</td></tr>
</xsl:template>

All the above is fine if that example is your entire XML document, but if there's more than one a element within the document each with its own set of b elements that need grouping independently, then the key needs to be more complex. The usual trick here is to use the generate-id of the parent a node as part of the grouping key value for its b children:

<xsl:key name="bsByParentAndId" match="a/b" use="concat(generate-id(..), '|', @id)" />

and for the Muenchian grouping expression

<xsl:template match="a">
  <xsl:apply-templates select="b[generate-id()
                               = generate-id(key('bsByParentAndId', concat(
                                   generate-id(current()), '|', @id))[1])]"
                       mode="group"/>
</xsl:template>

For the record, if you could use XSLT 2.0 then it becomes significantly easier. No need to define a complex key, you simply use for-each-group

<xsl:template match="a">
  <xsl:for-each-group select="b" group-by="@id">
    <xsl:sort select="current-grouping-key()" />
    <table>
      <xsl:apply-templates select="current-group()" />
    </table>
  </xsl:for-each-group>
</xsl:template>

<xsl:template match="b">
  <tr><td>...</td></tr>
</xsl:template>
Ian Roberts
  • 120,891
  • 16
  • 170
  • 183