So I want to convert the following using XSL
<doc>
<data id="priority" level="2" include="true">
<name>Priority</name>
</data>
<data id="cost" level="1" leveltype="number">
<name>Cost</name>
</data>
<data id="date" level="3" include="true">
<name>Date</name>
</data>
</doc>
To this
<doc>
<data id="priority">
<name>Priority</name>
</data>
<data id="cost">
<name>Cost</name>
</data>
<data id="date">
<name>Date</name>
</data>
<!-- ordering matters, though if necessary I can reorder this manually via the DOM instead of XSL -->
<levels>
<level id="cost" include="false" type="number"/>
<level id="priority" include="true"/>
<level id="date" include="true"/>
</level>
</doc>
Basically I want to take the level attributes and make them their own thing. A huge bonus would be if there were some way to remove the level number and use the order of the node instead to represent that.
This is a shorter and simpler solution using only templates (no <xsl:for-each>
):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<doc>
<xsl:apply-templates select="*"/>
<levels>
<xsl:apply-templates select="data" mode="level">
<xsl:sort select="@level" data-type="number"/>
</xsl:apply-templates>
</levels>
</doc>
</xsl:template>
<xsl:template match="data/@*[not(name()='id')]"/>
<xsl:template match="data" mode="level">
<level id="{@id}" include="{boolean(@include)}">
<xsl:if test="@leveltype">
<xsl:attribute name="type"><xsl:value-of select="@leveltype"/></xsl:attribute>
</xsl:if>
</level>
</xsl:template>
</xsl:stylesheet>
When applied on the provided XML document:
<doc>
<data id="priority" level="2" include="true">
<name>Priority</name>
</data>
<data id="cost" level="1" leveltype="number">
<name>Cost</name>
</data>
<data id="date" level="3" include="true">
<name>Date</name>
</data>
</doc>
the wanted, correct result is produced:
<doc>
<data id="priority">
<name>Priority</name>
</data>
<data id="cost">
<name>Cost</name>
</data>
<data id="date">
<name>Date</name>
</data>
<levels>
<level id="cost" include="false" type="number"/>
<level id="priority" include="true"/>
<level id="date" include="true"/>
</levels>
</doc>
Explanation:
Using and overriding the identity rule/template.
Using mode="level"
to generate the second part of the result-document.
Just a variant:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="doc">
<doc>
<!-- build and sort data nodes -->
<xsl:for-each select="data">
<xsl:sort select="@id"/>
<data id="{@id}">
<xsl:copy-of select="name"/>
</data>
</xsl:for-each>
<!-- build and sort levels -->
<levels>
<xsl:for-each select="data">
<xsl:sort select="@id"/>
<level id="{@id}" include="{boolean(@include)}">
<xsl:if test="@leveltype">
<xsl:attribute name="type">
<xsl:value-of select="@leveltype"/>
</xsl:attribute>
</xsl:if>
</level>
</xsl:for-each>
</levels>
</doc>
</xsl:template>
</xsl:stylesheet>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With