Flatten nested XML attributes to elements

218 views Asked by At

I need to create an XSLT file to transform my XML from attribute rich to element based to load it into an Access DB, but I'm struggling to see how to combine the attributes in the nested elements.
Any help would be appreciated.

Current data:

<device id="11281">
    <mv t="COS_PHI" v="0.999"/>
    <mv t="ERROR"/>
    <mv t="E_DAY" v="104010"/>
    -<mv t="I_AC">
        <sv t="1" v="41.9935"/>
        <sv t="2" v="41.8"/>
        <sv t="3" v="41.929"/>
    </mv>
    -<mv t="I_DC">
        <sv t="1" v="12.9839"/>
        <sv t="2" v="12.7774"/>
        <sv t="3" v="12.6258"/>
        <sv t="4" v="6.69032"/>
    </mv>
</device>

Required format:

<device>
    <id>11281</id>
    <COS_PHI>0.999</COS_PHI>
    <ERROR></ERROR>
    <E_DAY>104010</E-DAY>
    <I_AC_1>41.9935</I_AC_1>
    <I_AC_2>41.8</I_AC_2>
    <I_AC_3>41.929</I_AC_3>
    <I_DC_1>12.9839</I_DC_1>
    <I_DC_2>12.7774</I_DC_2>
    <I_DC_3>12.6258</I_DC_3>
    <I_DC_4>6.69032</I_DC_4>
</device>
1

There are 1 answers

0
zx485 On

The following XSLT-1.0 file will do the job:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
  <xsl:template match="text()" />             <!-- removes all free text() nodes -->

  <xsl:template match="mv[not(child::*)]">    <!-- transforms all <mv> elements without children -->
    <xsl:element name="{@t}">
      <xsl:value-of select="@v" />
    </xsl:element>
    <xsl:apply-templates select="sv" />       <!-- visits sub-nodes -->
  </xsl:template> 

  <xsl:template match="sv">                   <!-- creates elements with 'mv' parents -->
    <xsl:element name="{concat(../@t,'_',@t)}">
      <xsl:value-of select="@v" />
    </xsl:element>
  </xsl:template>   

  <xsl:template match="@id">                  <!-- handles 'id' attribute of root element -->
    <xsl:element name="id">
      <xsl:value-of select="." />
    </xsl:element>
  </xsl:template> 

  <xsl:template match="/device">              <!-- initiates process from root elements -->
    <xsl:copy>
      <xsl:apply-templates select="node()|@*" />
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>