Question

im new in xsl and I have some xml files with similar structure, like this:

 <catalog>
        <cd>
            <title>Empire Burlesque</title>
            <artist>Bob Dylan</artist>
            <country>USA</country>
            <company>Columbia</company>
            <price>10.90</price>
            <year>1985</year>
        </cd>
        <cd>
            <title>Hide your heart</title>
            <artist>Bonnie Tyler</artist>
            <country>UK</country>
            <company>CBS Records</company>
            <price>9.90</price>
            <year>1988</year>
        </cd>
    </catalog>

and other similar but with title and number of songs as description of the cd

<catalog>
    <cd>
        <title>Eros</title>
        <number_of_songs>12</number_of_songs>
    </cd>
    <cd>
        <title>One</title>
        <number_of_songs>12</number_of_songs>
    </cd>
</catalog>

What I need is to make an xsl file for all the xml, to make a table and takes the names of the child elements of cd, put them as and for each cd a row with the description. Like this:

║ Title ║Number of songs

║ Eros ║12

║ One ║12

cant post images yet:(

So far i've done this:

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

    <xsl:template match="/">
        <html>
            <body>
                <table style="border:2px solid #24343A; border-radius: 4px;">
                    <tr bgcolor="#4488A5" style="color:#ffffff; text-align:center;">

                        <xsl:for-each select="catalog/cd/child::*">
                            <th> <xsl:value-of select ="local-name()"/></th>
                        </xsl:for-each>
                    </tr>
                    <xsl:for-each select="catalog/cd">
                        <tr bgcolor="#8DC1D7" style="color:#21323B;">
                            <xsl:for-each select="child::*">
                                <td> <xsl:value-of select ="child::*"/></td>
                            </xsl:for-each>
                        </tr>
                    </xsl:for-each>
                </table>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>

But it doesnt work, i get the header, but it repeats many times, and cant get the value Thanks

Was it helpful?

Solution 2

You're not far off. Two things:

  • you don't want the headers to repeat - you only want the element names of the first CD, so the first expression becomes catalog/cd[1]/* (child::* can be replaced with *)
  • you want the text content for each value, not the child element. So use text() rather than child::*

See below:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <html>
            <body>
                <table style="border:2px solid #24343A; border-radius: 4px;">
                    <tr bgcolor="#4488A5" style="color:#ffffff; text-align:center;">
                        <xsl:for-each select="catalog/cd[1]/*">
                            <th> <xsl:value-of select ="local-name()"/></th>
                        </xsl:for-each>
                    </tr>
                    <xsl:for-each select="catalog/cd">
                        <tr bgcolor="#8DC1D7" style="color:#21323B;">
                            <xsl:for-each select="*">
                                <td> <xsl:value-of select="text()"/></td>
                            </xsl:for-each>
                        </tr>
                    </xsl:for-each>
                </table>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>

OTHER TIPS

Use the stylesheet below. I'd also recommend you study it closely.

Note that

  • you should avoid using xsl:for-each whenever inappropriate. Instead, use appyl-templates and write separate templates that match the elements concerned.
  • It is not clear whether you want to transform the first XML file you have shown. To arrive at your output, it is not needed.
  • local-name() makes sense only where elements have namespaces. To simply retrieve the name of an element, use name().

EDIT Modified stylesheet, suggested by @Tomalak.

Stylesheet

<?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:template match="/catalog">
    <html>
        <body>
            <table style="border:2px solid #24343A; border-radius: 4px;">
              <tr>
                 <th>Title</th>
                 <th>Number of songs</th>
              </tr>
              <xsl:apply-templates/>
            </table>
        </body>
    </html>
 </xsl:template>

 <xsl:template match="cd">
  <tr bgcolor="#4488A5" style="color:#ffffff; text-align:center;">
     <xsl:apply-templates select="title|number_of_songs" />
  </tr>
 </xsl:template>

 <xsl:template match="title|number_of_songs">
  <td>
     <xsl:value-of select="."/>
  </td>
 </xsl:template>

</xsl:stylesheet>

Output

<html>
 <body>
  <table style="border:2px solid #24343A; border-radius: 4px;">
     <tr>
        <th>Title</th>
        <th>Number of songs</th>
     </tr>
     <tr bgcolor="#4488A5" style="color:#ffffff; text-align:center;">
        <td>Eros</td>
        <td>12</td>
     </tr>
     <tr bgcolor="#4488A5" style="color:#ffffff; text-align:center;">
        <td>One</td>
        <td>12</td>
     </tr>
  </table>
 </body>
</html>

Output in Firefox

enter image description here


A possible enhancement

Your attempted XSLT retrieves the names of the header columns from the XML file. This is a proper way to do this:

 <tr>
   <th>
      <xsl:value-of select="name(cd[1]/title)"/>
   </th>
   <th>
      <xsl:value-of select="name(cd[1]/number_of_songs)"/>
   </th>
 </tr>

However, this copies the exact name of elements. So, in HTML, your column headers will be lowercased and the second one will look like: number_of_songs.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top