Reputation: 1014
I have the following XML:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<cars filter="yes">
<car>
<brand>Volkswagen</brand>
<make>Golf</make>
<wheels>4</wheels>
<extras hifi="yes" ac="no"/>
</car>
</cars>
I want to flatten the element <car>
so that it only has attributes - no more child elements!
So far I've produced this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="cars">
<cars>
<xsl:apply-templates/>
</cars>
</xsl:template>
<xsl:template match="car">
<car>
<xsl:for-each select="*">
<xsl:attribute name="{name()}">
<xsl:value-of select="text()"/>
</xsl:attribute>
</xsl:for-each>
</car>
</xsl:template>
</xsl:stylesheet>
This results in:
<cars>
<car brand="Volkswagen" make="Golf" wheels="4" extras=""/>
</cars>
Problems:
Expected result:
<cars filter="yes">
<car brand="Volkswagen" make="Golf" wheels="4" hifi="yes" ac="no"/>
</cars>
Upvotes: 0
Views: 42
Reputation: 70598
For the first issue, with the filter
attribute going missing, you can solve this by using the identity template instead of a specific template for cars
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
For extra
appearing as an attribute, can the select statement to only select elements with text in
<xsl:for-each select="*[normalize-space()]">
And finally, for the attributes of extras
, add another for-each to pick these up.
<xsl:for-each select="*/@*">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="car">
<car>
<xsl:for-each select="*[normalize-space()]">
<xsl:attribute name="{name()}">
<xsl:value-of select="text()"/>
</xsl:attribute>
</xsl:for-each>
<xsl:for-each select="*/@*">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
</car>
</xsl:template>
</xsl:stylesheet>
In fact, the two xsl:for-each
statements could be combined here
<xsl:template match="car">
<car>
<xsl:for-each select="*[normalize-space()]|*/@*">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
</car>
</xsl:template>
Note that you this assumes two different child elements of car
don't have the same attribute names.
Upvotes: 1