0
votes

I have XML file having structure like this-

<products>
 <product>
  <id>1</id>
  <type>fruits</type>
  <varieties>
   <variety>
    <id>a</id>
    <cost>100</cost>
    <availability>Y</availability>
   </variety>
   <variety>
    <id>b</id>
    <cost>75</cost>
    <availability>N</availability>
   </variety>
  </varieties>
 </product>
 <product>
  <id>2</id>
  <type>vegetables</type>
  <varieties>
   <variety>
    <id>c</id>
    <cost>50</cost>
    <availability>Y</availability>
   </variety>
   <variety>
    <id>d</id>
    <cost>55</cost>
    <availability>Y</availability>
   </variety>
  </varieties>
 </product>
</products>

I want to restructure the given XML based on products/product/id. The expected output would be as follows-

<html>
 <body>
  <table border="1">
  <tr> 
   <td>1</td>
   <td>fruits</td>
   <td>a</td>
   <td>100</td>
   <td>Y</td>
  </tr> 
  <tr> 
   <td>1</td>
   <td>fruits</td>
   <td>b</td>
   <td>75</td>
   <td>N</td>
  </tr>  
  <tr> 
   <td>2</td>
   <td>vegetables</td>
   <td>c</td>
   <td>50</td>
   <td>Y</td>
  </tr> 
  <tr> 
   <td>2</td>
   <td>vegetables</td>
   <td>d</td>
   <td>55</td>
   <td>Y</td>
  </tr>
  </table>
 </body>
</html>

I have tried this XSLT but it is not returning the expected xml.

<xsl:stylesheet version="1.0"    
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
 <products>
  <product>
   <varieties>
    <xsl:apply-templates select="products/product/varieties/variety" /> 
   </varieties>
  </product>
 </products>
</xsl:template>
<xsl:template match="products" >
<html>
 <body>
  <table border="1">
   <xsl:for-each select="products/product">
    <tr>
     <td><xsl:value-of select="id" /></td>
     <td><xsl:value-of select="type" /></td>
     <xsl:for-each select="varieties/variety" >
      <td><xsl:value-of select="id" /></td>
      <td><xsl:value-of select="cost" /></td>
      <td><xsl:value-of select="availability" /></td>
     </xsl:for-each>
    </tr> 
   </xsl:for-each>
  </table>
 </body>
</html>
</xsl:template>      
</xsl:stylesheet>
1
You are applying templates to 'variety` - but you don't have a template matching variety. You have a template matching root - but there is no element named root. And the contents of the template matching / (which is the only template that gets executed) does not match your intended output. - michael.hor257k
Note also that XML is case-sensitive: variety does not match Variety. - michael.hor257k
Hi @michael.hor257k i have modified my XSLT. could you please suggest the changes? - QA Testing
No. This is really a trivial task. Create a row for each variety, and use <xsl:value-of select="../../id" /> and <xsl:value-of select="../../type"/> to get the ancestor product's id and type respectively. - michael.hor257k

1 Answers

0
votes

You need to match against /products, so the below should give you what you want

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/products" >
<html>
 <body>
  <table border="1">
   <xsl:for-each select="product">
    <tr>
     <td><xsl:value-of select="id" /></td>
     <td><xsl:value-of select="type" /></td>
     <xsl:for-each select="varieties/variety" >
      <td><xsl:value-of select="id" /></td>
      <td><xsl:value-of select="cost" /></td>
      <td><xsl:value-of select="availability" /></td>
     </xsl:for-each>
    </tr> 
   </xsl:for-each>
  </table>
 </body>
</html>
</xsl:template>      
</xsl:stylesheet>

IMHO your html output could be quite messy if you have varying numbers of varieties for each product. I don't know your application but you may want to consider using rowspan on the first 2 columns based on count of varieties and have one row per variety.

Updated based on OP feedback

I've included the code to span the repeated data, if you don't like that then feel free to remove the if start and end tags and remove the rowspan attribute

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/products" >
<html>
 <body>
     <table border="1">
        <xsl:for-each select="product">
        <xsl:variable name="ProductID" select="id"/>
        <xsl:variable name="ProductType" select="type"/>
        <xsl:variable name="VarietyCount" select="count(varieties/variety)"/>
         <xsl:for-each select="varieties/variety" >
            <tr>
                <xsl:if test="position() = 1">
                     <td rowspan="{$VarietyCount}"><xsl:value-of select="$ProductID" /></td>
                     <td rowspan="{$VarietyCount}"><xsl:value-of select="$ProductType" /></td>
                </xsl:if>
                  <td><xsl:value-of select="id" /></td>
                  <td><xsl:value-of select="cost" /></td>
                  <td><xsl:value-of select="availability" /></td>
            </tr> 
            </xsl:for-each>
        </xsl:for-each>
     </table>
 </body>
</html>
</xsl:template>      
</xsl:stylesheet>