Reputation: 13
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
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
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