Reputation:
I am trying to transform input XML documents attributes to elements using the following stylesheet:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml"/>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:for-each select="@*[name()!='type']">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
However, despite correctly creating new elements based on each element attributes, "leaf" elements (i.e., with no children) have their text mixed with created elements. E.g.
Input XML
<order>
<id>12345</id>
<date>Today</date>
<location country="PT">LX</location>
<location country="ES">Barcelona</location>
<items type="array" stock="true">
<item>
<id type="array">item1</id>
<spec>
<color type="array">brown</color>
<price currency="euro">20</price>
<usage>office</usage>
<usage>home</usage>
</spec>
</item>
</items>
</order>
Output XML
<order>
<id>12345</id>
<date>Today</date>
<location>
<country>PT</country>LX
</location>
<location>
<country>ES</country>Barcelona
</location>
<items>
<stock>true</stock>
<item>
<id>item1</id>
<spec>
<color>brown</color>
<price>
<currency>euro</currency>20
</price>
<usage>office</usage>
<usage>home</usage>
</spec>
</item>
</items>
</order>
What I want
...
<location>
<text>LX</text>
<country>PT</country>
</location>
<location>
<text>Barcelona</text>
<country>ES</country>
</location>
...
<price>
<text>20</text>
<currency>euro</currency>
</price>
...
What I tried
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml"/>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:for-each select="@*[name()!='type']">
<xsl:element name="text">
<xsl:value-of select=".."/>
</xsl:element>
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Problem Despite correctly creating new element tag
<text>20</text>
it also creates "text" element on "non-leaf" elements (i.e., elements with children), getting for the provided example:
...
<items>
<text>item1brown20officehome</text>
<stock>true</stock>
<item>
<id>item1</id>
<spec>
<color>brown</color>
<price>
<text>20</text>
<currency>euro</currency>20
</price>
<usage>office</usage>
<usage>home</usage>
</spec>
</item>
</items>
...
I do not want
<text>item1brown20officehome</text>
Any ideas? Thanks in advance.
Upvotes: 2
Views: 1568
Reputation: 52848
I'm not sure if this creates the exact output you want because you only included a portion of your desired output, but try something like this...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="text()"/>
<xsl:apply-templates select="@*|*|comment()|processing-instruction()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[@*]/text()">
<text><xsl:value-of select="."/></text>
</xsl:template>
<xsl:template match="@*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Fiddle: http://xsltfiddle.liberty-development.net/jyH9rLX
What if I want to keep only attributes whose name is "type" as attributes (not converting them to elements, but keeping them as they were originally). How could I achieve such goal?
Updated XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="@type|text()"/>
<xsl:apply-templates
select="@*[not(name()='type')]|*|comment()|processing-instruction()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[@*]/text()">
<text><xsl:value-of select="."/></text>
</xsl:template>
<xsl:template match="@type">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="@*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Updated fiddle: http://xsltfiddle.liberty-development.net/jyH9rLX/2
Upvotes: 1