I am trying to use XSLT to group data from an XML document into two categories. Specifically, given a list of three items, I want the XSLT to export the three items as two different groups (food & buildings). I'm attempting to do this by grouping the data by substrings.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<items>
<Skyscraper>Willis Tower</Skyscraper>
<Small_fruit>blueberry</Small_fruit>
<Big_fruit>watermelon </Big_fruit>
</items>
What I tried (XSLT):
<?xml version="1.1" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
<xsl:output method="xml"/>
<xsl:template match = "/">
<items>
<xsl:element name = "Fruit">
<xsl:for-each-group select="//items" group-by="*[contains(local-name(), 'fruit')]">
<xsl:copy-of select = "."/>
<xsl:value-of select="current-grouping-key()"/>
<xsl:if test = "not(position() = last())">,</xsl:if>
</xsl:for-each-group>
</xsl:element>
</items>
</xsl:template>
</xsl:stylesheet>
What I expected (XML):
<?xml version="1.0" encoding="UTF-8"?>
<items>
<Building>
<Skyscraper>Willis Tower</Skyscraper>
</Building>
<Food>
<Small_fruit>blueberry</Small_fruit>
<Big_fruit>watermelon </Big_fruit>
</Food>
</items>
Edited XSLT
<?xml version="1.1" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
<!-- airport XSLT -->
<xsl:output method="xml"/>
<xsl:template match = "/">
<items>
<xsl:element name = "fruit">
<xsl:for-each-group select="/items/*" group-by="*[contains(local-name(), 'fruit')]">
<xsl:copy-of select = "."/>
<xsl:value-of select="current-grouping-key()"/>
<xsl:if test = "not(position() = last())">
</xsl:if>
</xsl:for-each-group>
</xsl:element>
<xsl:element name = "building">
<xsl:for-each-group select="/items/*" group-by="*[contains(local-name(), 'sky')]">
<xsl:copy-of select = "."/>
<xsl:value-of select="current-grouping-key()"/>
<xsl:if test = "not(position() = last())">
</xsl:if>
</xsl:for-each-group>
</xsl:element>
</items>
</xsl:template>
</xsl:stylesheet>
Try it this way?
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/items">
<items>
<xsl:for-each-group select="*" group-by="contains(local-name(), 'fruit')">
<xsl:element name="{if(current-grouping-key()) then 'Food' else 'Building'}">
<xsl:copy-of select="current-group()"/>
</xsl:element>
</xsl:for-each-group>
</items>
</xsl:template>
</xsl:stylesheet>
P.S. You could also do simply:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/items">
<xsl:variable name="fruit" select="*[contains(local-name(), 'fruit')]" />
<items>
<Building>
<xsl:copy-of select="* except $fruit"/>
</Building>
<Food>
<xsl:copy-of select="$fruit"/>
</Food>
</items>
</xsl:template>
</xsl:stylesheet>
The difference is this will always create both groups, even if one (or both) of them is empty.