1
votes

I'm really struggling transforming some XML. I'm usually OK with XSLT but it might be the root element causing me problems in the XML. I would really appreciate some guidance.

Existing XML:

<?xml version="1.0" ?>
<asx:abap xmlns:asx="http://www.testsrvXXXX.com/abdxml" version="1.0">
<asx:values>

<META>

 <XOUTPUT>
  <FIELDNAME>Make</FIELDNAME> 
  <OUTPUTLEN>000130</OUTPUTLEN> 
  <TYPE>CHAR</TYPE> 
  <SRC_TEXT>Make</SRC_TEXT> 
 </XOUTPUT>

 <XOUTPUT>
  <FIELDNAME>Model</FIELDNAME> 
  <OUTPUTLEN>000130</OUTPUTLEN> 
  <TYPE>CHAR</TYPE> 
  <SRC_TEXT>Car Model</SRC_TEXT> 
 <XOUTPUT>

 <XOUTPUT>
  <FIELDNAME>Engine</FIELDNAME> 
  <OUTPUTLEN>000130</OUTPUTLEN> 
  <TYPE>CHAR</TYPE> 
  <SRC_TEXT>Engine Size</SRC_TEXT> 
 </XOUTPUT>

 <XOUTPUT>
  <FIELDNAME>Type</FIELDNAME> 
  <OUTPUTLEN>000130</OUTPUTLEN> 
  <TYPE>CHAR</TYPE> 
  <SRC_TEXT>Fuel Type</SRC_TEXT> 
 </XOUTPUT>

 <XOUTPUT>
  <FIELDNAME>Colour</FIELDNAME> 
  <OUTPUTLEN>000130</OUTPUTLEN> 
  <TYPE>CHAR</TYPE> 
  <SRC_TEXT>Paint Colour</SRC_TEXT> 
 </XOUTPUT>

 <XOUTPUT>
  <FIELDNAME>Doors</FIELDNAME> 
  <OUTPUTLEN>000130</OUTPUTLEN> 
  <TYPE>NUM</TYPE> 
  <SRC_TEXT>Door Count</SRC_TEXT> 
 </XOUTPUT>

 <XOUTPUT>
  <FIELDNAME>Speed</FIELDNAME> 
  <OUTPUTLEN>000130</OUTPUTLEN> 
  <TYPE>NUM</TYPE> 
  <SRC_TEXT>Top Speed</SRC_TEXT> 
 </XOUTPUT>

 <XOUTPUT>
  <FIELDNAME>Service</FIELDNAME> 
  <OUTPUTLEN>000130</OUTPUTLEN> 
  <TYPE>CHAR</TYPE> 
  <SRC_TEXT>Service Intervals</SRC_TEXT> 
 </XOUTPUT>

</META>

<Y_OUTPUT>
 <item>
  <Make>Volkswagen</Make> 
  <Service>20000 KM</Service> 
  <Model>Golf</Model> 
  <Speed>190 KPH</Speed> 
  <Engine>1400 CC</Engine> 
  <Type>Diesel</Type> 
  <Colour>Black</Colour> 
  <Doors>3</Doors> 
 </item>
 <item>
  <Make>Ford</Make> 
  <Service>15000 KM</Service> 
  <Model>Fiesta</Model> 
  <Speed>180 KPH</Speed> 
  <Engine>1400 CC</Engine> 
  <Type>Petrol</Type> 
  <Colour>Red</Colour> 
  <Doors>5</Doors> 
 </item>
</Y_OUTPUT>
</asx:values>
</asx:abap>

The transformed XML needs to look like:

<Cars>
  <Record>
   <Make>Volkswagen</Make>
   <Service>20000 KM</Service>
   <Model>Golf</Model>
   <Speed>190 KPH</Speed>
   <Engine>1400 CC</Engine>
   <Type>Diesel</Type>
   <Colour>Black</Colour>
   <Doors>3</Doors>
 </Record>
 <Record>
   <Make>Ford</Make>
   <Service>15000 KM</Service>
   <Model>Fiesta</Model>
   <Speed>180 KPH</Speed>
   <Engine>1400 CC</Engine>
   <Type>Petrol</Type>
   <Colour>Red</Colour>
   <Doors>5</Doors>
 </Record>
</Cars>

My XSLT:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="Y_OUTPUT/ITEM">
<Cars>
        <Record>
            <Make><xsl:value-of select="Make"/></Make>
            <Service><xsl:value-of select="Service"/></Service> 
            <Model><xsl:value-of select="Model"/></Model>
            <Speed><xsl:value-of select="Speed"/></Speed>
            <Engine><xsl:value-of select="Engine"/></Engine> 
            <Type><xsl:value-of select="Type"/></Type>
            <Colour><xsl:value-of select="Colour"/></Colour> 
            <Doors><xsl:value-of select="Doors"/></Doors> 
        </Record>
</Cars>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
1
After fixing 2 things, it works: first, your second XOUTPUT tag is not closed properly (the / is missing from the closing tag). Second, in the xslt, I just changed the select of the for-each to: "//Y_OUTPUT/item"Maria Ivanova

1 Answers

0
votes

I think the problem is that you match / in your template, but then start the xsl:for-each select with Y_OUTPUT and Y_OUTPUT isn't the root element in your XML input.

You can update your xsl:for-each to start where the template match left off (the current context).

You can also add a second xsl:for-each to handle the creation of the child elements without namespace declarations...

<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="/*">
    <Cars>
      <xsl:for-each select="*/Y_OUTPUT/item">
        <Record>
          <xsl:for-each select="*">
            <xsl:element name="{local-name()}">
              <xsl:value-of select="."/>
            </xsl:element>
          </xsl:for-each>
        </Record>
      </xsl:for-each>
    </Cars>    
  </xsl:template>

</xsl:stylesheet>

An alternative would be to use xsl:apply-templates.

It's a few more lines, but a good pattern (push) to use when creating more complicated transforms...

<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="/*">
    <Cars>
      <xsl:apply-templates select="*/Y_OUTPUT/item"/>
    </Cars>    
  </xsl:template>

  <xsl:template match="item">
    <Record>
      <xsl:apply-templates/>
    </Record>
  </xsl:template>

  <xsl:template match="item/*">
    <xsl:element name="{local-name()}">
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

It helps to fully understand XSLTs built-in template rules to know how the template above is working.