Reputation: 1323
My xml structure contains a program as the parent of both certificates and courses. I want to split the structure up to create an independent listing of certificates and courses without the common program parent. The original structure is:
<root>
<program>
<program-name>Math</program-name>
<certificate>...</certificate> <!-- There can 0 or more of these -->
<course>...</course> <!-- There can 0 or more of these -->
</program>
<program>
...
</program>
</root>
The output should look like so:
<root>
<program-courses>
<program>
<program-name>Math</program-name>
<course/> <!-- There can 0 or more of these -->
</program>
...
</program-courses>
<program-certificates>
<program>
<program-name>Math</program-name>
<certificate/> <!-- There can 0 or more of these -->
</program>
...
</program-certificates>
</root>
Update: Answered using Paul's suggestion for using a mode this is what the relevant portion of the xslt became:
<xsl:template match="root">
<xsl:element name="root">
<xsl:element name="program-courses">
<xsl:apply-templates select="root/program-area" mode="course"/>
</xsl:element>
<xsl:element name="program-certificates">
<xsl:apply-templates select="root/program-area" mode="certificate"/>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="program-area" mode="course">
<xsl:element name="program-area">
<!-- Get the name here -->
<xsl:element name="course">
<xsl:apply-templates select="course"/>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="program-area" mode="certificate">
<xsl:element name="program-area">
<!-- Get the name here -->
<xsl:element name="course">
<xsl:apply-templates select="certificate"/>
</xsl:element>
</xsl:element>
</xsl:template>
Note that this solution is pared down from the actual one so it may not work as is against the original input.
Upvotes: 2
Views: 321
Reputation:
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="program">
<xsl:apply-templates select="course[1]|certificate[1]"/>
</xsl:template>
<xsl:template match="course[1]">
<program-courses>
<program>
<xsl:apply-templates select="../*[not(self::certificate)]"
mode="copy"/>
</program>
</program-courses>
</xsl:template>
<xsl:template match="certificate[1]">
<program-certificates>
<program>
<xsl:apply-templates select="../*[not(self::course)]"
mode="copy"/>
</program>
</program-certificates>
</xsl:template>
<xsl:template match="node()" mode="copy">
<xsl:call-template name="identity"/>
</xsl:template>
</xsl:stylesheet>
Output:
<root>
<program-certificates>
<program>
<program-name>Math</program-name>
<certificate>...</certificate>
</program>
</program-certificates>
<program-courses>
<program>
<program-name>Math</program-name>
<course>...</course>
</program>
</program-courses>
</root>
EDIT: If you want something more "push style" like your posted solution:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()" mode="certificate">
<xsl:call-template name="identity"/>
</xsl:template>
<xsl:template match="node()" mode="course">
<xsl:call-template name="identity"/>
</xsl:template>
<xsl:template match="program">
<program-courses>
<program>
<xsl:apply-templates mode="course"/>
</program>
</program-courses>
<program-certificates>
<program>
<xsl:apply-templates mode="certificate"/>
</program>
</program-certificates>
</xsl:template>
<xsl:template match="course" mode="certificate"/>
<xsl:template match="certificate" mode="course"/>
</xsl:stylesheet>
Upvotes: 1
Reputation: 6956
Selecting program elements
, You could use @mode
(on apply-templates
and a corresponding template
) to differentiate between whether you are operating within the output of program-courses
or program-certificates
From root
, you could select program/course
or program/certificate
to generate the output program
.
From root
, you could use for-each select="program"
and for the part that is intended to output program-courses
only extract the program-name
and course element
, and perform the corresponding extraction in the part that outputs program-certificates
.
Upvotes: 1