emyara
emyara

Reputation: 13

XSLT Rearrange or Regroup Nodes Efficiently

I'm trying to rearrange a set of XML nodes for further processing. The basic idea is that I need to change the grouping of my nodes using efficient XSLT processing.

The input structure I have is:

<all_nodes>
  <student>
    <math>
      <record>
        <detail1/>
        <detail2/>
      </record>
    </math>
    <science>
      <record>
        <detail1/>
        <detail2/>
      </record>
    </science>
    <history>
      <record>
        <detail1/>
        <detail2/>
      </record>
    </history>
  </student>
  <student>
    <math>
      <record>
        <detail1/>
        <detail2/>
      </record>
    </math>
    <science>
      <record>
        <detail1/>
        <detail2/>
      </record>
    </science>
    <history>
      <record>
        <detail1/>
        <detail2/>
      </record>
    </history>
  </student>
</all_nodes>

The desired output is grouped by subjects instead. Note that the student node is dropped as it will not be needed I just need the record nodes with common subject parent node grouped together:

<all_nodes>
  <math>
    <record>
      <detail1/>
      <detail2/>
    </record>
    <record>
      <detail1/>
      <detail2/>
    </record>
  </math>
  <science>
    <record>
      <detail1/>
      <detail2/>
    </record>
    <record>
      <detail1/>
      <detail2/>
    </record>
  </science>
  <history>
    <record>
      <detail1/>
      <detail2/>
    </record>
    <record>
      <detail1/>
      <detail2/>
    </record>
  </history>
</all_nodes>

I was able to achieve the desired output by using the following code, however I think there may be a better approach to this. Can you advise on how I can improve the code?

<xsl:template match="/">
    <xsl:call-template name="math"/>
    <xsl:call-template name="science"/>
    <xsl:call-template name="history"/>
</xsl:template>

<xsl:template name="math">
    <xsl:element name="math">
        <xsl:apply-templates select="//math/record" />
    </xsl:element>
</xsl:template>

<xsl:template name="science">
    <xsl:element name="science">
        <xsl:apply-templates select="//science/record" />
    </xsl:element>
</xsl:template>

<xsl:template name="history">
    <xsl:element name="history">
        <xsl:apply-templates select="//history/record" />
    </xsl:element>
</xsl:template>

<xsl:template match="record">
    <xsl:copy-of select="."/>
</xsl:template>

Thank you!

Upvotes: 1

Views: 410

Answers (2)

Mads Hansen
Mads Hansen

Reputation: 66724

A more generic implementation using XSLT 2.0 that would support any subject without explicitly enumerating them in the stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/*">
        <xsl:copy>            
            <xsl:for-each-group select="*/*/*" group-by="local-name(..)">
                <xsl:element name="{current-grouping-key()}">
                    <xsl:copy-of select="current-group()"/>
                </xsl:element>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Upvotes: 0

michael.hor257k
michael.hor257k

Reputation: 116992

If it's always just the three known subjects, then you could do this quite simply as:

[deleted]

Edit: actually, using a key is not likely to bring a significant advantage here, so why not just make it even simpler:

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:template match="/all_nodes">
    <xsl:copy>
        <math>
            <xsl:copy-of select="student/math/record"/>
        </math>
        <science>
            <xsl:copy-of select="student/science/record"/>
        </science>
        <history>
            <xsl:copy-of select="student/history/record"/>
        </history>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Upvotes: 1

Related Questions