How do I split info into sections via XSLT?
February 23, 2007 3:29 PM Subscribe
I need to take an XML list and split it into sections/subsections by keying off a child element. Samples and
I need to change my source XML into an XML output that will: (1) split out sections per children elements; (2) split out child sections per titles (e.g., alphabetically); and (3) title the child sections per alphabet (e.g, A, B, C...). Actually, it'll be easier to explain with these examples:
Example Source XML:
AAA
AAA definition
< !--where cat1, cat2, etc. specify which termdef go in which output section. some termdefs will be in multiple sections -->
>
ABB
ABB definition
BBB
BBB definition
BCC
BCC definition
Example Output XML:
Cat1 Defs
A Defs
AAA
AAA definition
ABB
ABB definition
Cat2 Defs
A Defs
ABB
ABB definition
B Defs
BBB
BBB definition
Cat3 Defs
A Defs
AAA
AAA definition
B Defs
BBB
BBB definition
BCC
BCC definition
Any ideas? I've been looking at this too long...
I need to change my source XML into an XML output that will: (1) split out sections per children elements; (2) split out child sections per titles (e.g., alphabetically); and (3) title the child sections per alphabet (e.g, A, B, C...). Actually, it'll be easier to explain with these examples:
Example Source XML:
>
Example Output XML:
Any ideas? I've been looking at this too long...
Response by poster: Arg: no wonder you didn't understand the question, my characters didn't escape for the code. Trying again. BTW, I'm using XSLT.
I need to change my source XML into an XML output that will: (1) split out sections per children elements; (2) split out child sections per titles (e.g., alphabetically); and (3) title the child sections per alphabet (e.g, A, B, C...). Actually, it'll be easier to explain with these examples:
Example Source XML:
<document>
<termdef>
<term>AAA</term>
<def>AAA definition</def>
<cat1/> <!--Where cat1, cat2, etc. specify which termdef go in which output section. Some termdefs will be in multiple sections -->
<cat3/>
</termdef>
<termdef>
<term>ABB</term>
<def>ABB definition</def>
<cat1/>
<cat2/>
</termdef>
<termdef>
<term>BBB</term>
<def>BBB definition</def>
<cat2/>
<cat3/>
</termdef>
<termdef>
<term>BCC</term>
<def>BCC definition</def>
<cat3/>
</termdef>
</document>
Example Output XML:
<document>
<section>
<title>Cat1 Defs</title>
<subsection>
<title>A Defs</title>
<termdef>
<term>AAA</term>
<def>AAA definition</def>
</termdef>
<termdef>
<term>ABB</term>
<def>ABB definition</def>
</termdef>
</subsection>
</section>
<section>
<title>Cat2 Defs</title>
<subsection>
<title>A Defs</title>
<termdef>
<term>ABB</term>
<def>ABB definition</def>
</termdef>
</subsection>
<subsection>
<title>B Defs</title>
<termdef>
<term>BBB</term>
<def>BBB definition</def>
</termdef>
</subsection>
<section>
<section>
<title>Cat3 Defs</title>
<subsection>
<title>A Defs</title>
<termdef>
<term>AAA</term>
<def>AAA definition</def>
</termdef>
</subsection>
<subsection>
<title>B Defs</title>
<termdef>
<term>BBB</term>
<def>BBB definition</def>
</termdef>
<termdef>
<term>BCC</term>
<def>BCC definition</def>
</termdef>
</subsection>
</section>
</document> ]]>
posted by sfkiddo at 4:06 PM on February 23, 2007
I need to change my source XML into an XML output that will: (1) split out sections per children elements; (2) split out child sections per titles (e.g., alphabetically); and (3) title the child sections per alphabet (e.g, A, B, C...). Actually, it'll be easier to explain with these examples:
Example Source XML:
<document>
<termdef>
<term>AAA</term>
<def>AAA definition</def>
<cat1/> <!--Where cat1, cat2, etc. specify which termdef go in which output section. Some termdefs will be in multiple sections -->
<cat3/>
</termdef>
<termdef>
<term>ABB</term>
<def>ABB definition</def>
<cat1/>
<cat2/>
</termdef>
<termdef>
<term>BBB</term>
<def>BBB definition</def>
<cat2/>
<cat3/>
</termdef>
<termdef>
<term>BCC</term>
<def>BCC definition</def>
<cat3/>
</termdef>
</document>
Example Output XML:
<document>
<section>
<title>Cat1 Defs</title>
<subsection>
<title>A Defs</title>
<termdef>
<term>AAA</term>
<def>AAA definition</def>
</termdef>
<termdef>
<term>ABB</term>
<def>ABB definition</def>
</termdef>
</subsection>
</section>
<section>
<title>Cat2 Defs</title>
<subsection>
<title>A Defs</title>
<termdef>
<term>ABB</term>
<def>ABB definition</def>
</termdef>
</subsection>
<subsection>
<title>B Defs</title>
<termdef>
<term>BBB</term>
<def>BBB definition</def>
</termdef>
</subsection>
<section>
<section>
<title>Cat3 Defs</title>
<subsection>
<title>A Defs</title>
<termdef>
<term>AAA</term>
<def>AAA definition</def>
</termdef>
</subsection>
<subsection>
<title>B Defs</title>
<termdef>
<term>BBB</term>
<def>BBB definition</def>
</termdef>
<termdef>
<term>BCC</term>
<def>BCC definition</def>
</termdef>
</subsection>
</section>
</document> ]]>
posted by sfkiddo at 4:06 PM on February 23, 2007
Best answer: This should work:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="categories" match="/document/termdef/*[name() != 'term' and name() != 'def']" use="name()"/>
<xsl:key name="definitions-by-initial" match="/document/termdef" use="substring(term, 1, 1)"/>
<xsl:template match="/">
<document>
<xsl:for-each select="document/termdef/*[name() != 'term' and name() != 'def'][generate-id() = generate-id(key('categories', name())[1])]">
<xsl:sort select="name()" data-type="text" order="ascending"/>
<section>
<title><xsl:value-of select="concat(name(), ' Defs')"/></title>
<xsl:variable name="category" select="self::*"/>
<xsl:for-each select="/document/termdef[child::*[name() = name($category)]][generate-id() = generate-id(key('definitions-by-initial', substring(term, 1, 1))[child::*[name() = name($category)]][1])]">
<xsl:sort select="term" data-type="text" order="ascending"/>
<subsection>
<title><xsl:value-of select="concat(substring(term, 1, 1), ' Defs')"/></title>
<xsl:apply-templates select="key('definitions-by-initial', substring(term, 1, 1))[child::*[name() = name($category)]]"/>
</subsection>
</xsl:for-each>
</section>
</xsl:for-each>
</document>
</xsl:template>
<xsl:template match="termdef">
<xsl:copy>
<xsl:copy-of select="term|def"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
posted by kyten at 9:48 PM on February 23, 2007
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="categories" match="/document/termdef/*[name() != 'term' and name() != 'def']" use="name()"/>
<xsl:key name="definitions-by-initial" match="/document/termdef" use="substring(term, 1, 1)"/>
<xsl:template match="/">
<document>
<xsl:for-each select="document/termdef/*[name() != 'term' and name() != 'def'][generate-id() = generate-id(key('categories', name())[1])]">
<xsl:sort select="name()" data-type="text" order="ascending"/>
<section>
<title><xsl:value-of select="concat(name(), ' Defs')"/></title>
<xsl:variable name="category" select="self::*"/>
<xsl:for-each select="/document/termdef[child::*[name() = name($category)]][generate-id() = generate-id(key('definitions-by-initial', substring(term, 1, 1))[child::*[name() = name($category)]][1])]">
<xsl:sort select="term" data-type="text" order="ascending"/>
<subsection>
<title><xsl:value-of select="concat(substring(term, 1, 1), ' Defs')"/></title>
<xsl:apply-templates select="key('definitions-by-initial', substring(term, 1, 1))[child::*[name() = name($category)]]"/>
</subsection>
</xsl:for-each>
</section>
</xsl:for-each>
</document>
</xsl:template>
<xsl:template match="termdef">
<xsl:copy>
<xsl:copy-of select="term|def"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
posted by kyten at 9:48 PM on February 23, 2007
Response by poster: Thanks, kyten! I'll give that a try today.
posted by sfkiddo at 11:24 AM on February 24, 2007
posted by sfkiddo at 11:24 AM on February 24, 2007
This thread is closed to new comments.
posted by nakedsushi at 4:01 PM on February 23, 2007