I have below input XML message that needs to be converted to nested XML with multiple attributes.
<S1>
<P>23432</P>
<Su>BG</Su>
<S2>
<P>cc</P>
<Su>B</Su>
<S3>
<P>427013947</P>
<Su>RM</Su>
<F>MDF</F>
<I>427113948</I>
<C>true</C>
<T></T>
<A>xxx</A>
</S3>
<F></F>
<I>427013947</I>
<C>true</C>
<T></T>
<A>xxxsx</A>
</S2>
<S2>
<P>cc</P>
<Su>FL</Su>
<F>1</F>
<I>427814536</I>
<C>true</C>
<T></T>
<A>xxxsx</A>
</S2>
<S2>
<P>cc</P>
<Su>G</Su>
<F></F>
<I>444</I>
<C>true</C>
<T></T>
<A>xxxsx</A>
</S2>
<F>1</F>
<I>cc</I>
<C>true</C>
<T></T>
<A>xxxs</A>
<B></B>
</S1>
Output XML
<S1 P="23432" Su="BG" F="1" I="cc" C="true" T="" A="xxxs" B="">
<S2 P="cc" Su="B" F="" I="427013947" C="true" T="" A="xxxsx">
<S3 P="427013947" Su="RM" F="MDF" I="427113948" C="true" T="" A="xxx"></S3>
</S2>
<S2 P="cc" Su="FL" F="1" I="427814536" C="true" T="" A="xxxsx"></S2>
<S2 P="cc" Su="G" F="" I="444" C="true" T="" A="xxxsx"></S2>
</S1>
All the S1, S2, S3 tags should be kept as it is while other child tags should add as attributes to the respective parent tag. Depth of the nested XML array goes up to 9 like S1, S2, S3 ...S9.
I tried to write an XSLT transformation and tried to use a recursive function, but I was not able to do that. Can someone help me to write an XSLT sheet to do this transformation?
I tried something like below(not completed), What would be the best approach to achieve this? The solution should be in XSLT 1.0.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" encoding="UTF-8" />
<xsl:template match="//S1">
<xsl:element name="S1">
<xsl:for-each select="*">
<xsl:if test="local-name()!='S1'">
<xsl:attribute name="{name()}" >
<xsl:value-of select="text()" />
</xsl:attribute>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="S2">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:for-each>
</xsl:element>
</xsl:template></xsl:stylesheet>
If you are limited to XSLT 1.0 then this gets awkward because of the need to distinguish between Su
and S3
.
However, if it can be assumed that any (and only) element that does not have a child element should be converted to an attribute then this can be done simply and elegantly:
XSLT 1.0
<xsl:stylesheet version="1.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="*[*]">
<xsl:copy>
<xsl:apply-templates select="*[not(*)]"/>
<xsl:apply-templates select="*[*]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(*)]">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>