Reputation: 13
I'm trying to reformat some XML using XSLT. A snippet of my input looks like this:
<toggles>
<toggle toggleDisplayName="Charges">
<anotherElement attribute="value" />
<gridColumn sourceField.name="FIELD1" />
<gridColumn sourceField.name="FIELD2" />
<gridColumn sourceField.name="FIELD3" />
</toggle>
</toggles>
I want to wrap all 'gridColumn' elements into a single 'grid' element like so:
<toggles>
<toggle toggleDisplayName="Charges">
<anotherElement attribute="value" />
<grid>
<gridColumn sourceField.name="FIELD1" />
<gridColumn sourceField.name="FIELD2" />
<gridColumn sourceField.name="FIELD3" />
</grid>
</toggle>
</toggles>
My current XSLT is:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="toggle/gridColumn">
<grid>
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</grid>
</xsl:template>
<!--Identity transform for remaining-->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Right now this sort of works, but I get each 'gridColumn' element in its own 'grid' element. Is there any easy way to modify this so that I can obtain the aforementioned results?
Note: edited to clarify input XML.
Thanks in advance for any help!
Upvotes: 1
Views: 649
Reputation: 122424
The simplest variant I can think of, which works if all the gridColumn
elements are adjacent (forming one contiguous block with no other intervening elements) in the original input XML:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*" />
<xsl:output method="xml" indent="yes"/>
<xsl:template match="toggle">
<xsl:copy>
<!-- apply templates to everything except the second and subsequent
gridColumn child elements -->
<xsl:apply-templates select="@* | node()[not(self::gridColumn)]
| gridColumn[1]" />
</xsl:copy>
</xsl:template>
<!-- this template will be called for just the first gridColumn within
a toggle... -->
<xsl:template match="gridColumn">
<grid>
<!-- ... and will gather all its sibling gridColumn elements under the
new grid element -->
<xsl:copy-of select="../gridColumn" />
</grid>
</xsl:template>
<!--Identity transform for remaining-->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If the gridColumn
elements are not all adjacent you'll find that they've all been gathered together into a single <grid>
at the place where the first gridColumn
appeared in the input XML.
Upvotes: 1
Reputation: 59640
I came up with this solution. Please check if it fulfills all your requirements.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes" />
<!-- Process toggle separately, because its childredn cannot simply be copied -->
<xsl:template match="toggle">
<xsl:copy>
<xsl:apply-templates select="./@*" /> <!-- For some unknown reason I needed this -->
<xsl:apply-templates />
<grid>
<xsl:for-each select="gridColumn">
<xsl:copy-of select="." />
</xsl:for-each>
</grid>
</xsl:copy>
</xsl:template>
<xsl:template match="gridColumn">
<!-- Do nothing, because it is processed by for-each -->
</xsl:template>
<!--Identity transform for remaining-->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0