Trying to transform XML to HTML tables. I can find plenty of examples on creating an XSLT style sheet for XML where the format is like this:
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
</cd>
</catalog>
But what if the format is like this, as it is in my dataset?
<DataObjects>
<ObjectSelect>
<mdNm>my-catalog</mdNm>
<meNm>Catalog Selection A1</meNm>
</ObjectSelect>
<DataInstances>
<DataInstance>
<instanceId>Cd</instanceId>
<field name='title'>Empire Burlesque</field>
<field name='artist'>Bob Dylan</field>
<field name='country'>USA</field>
<field name='company'>Columbia</field>
</DataInstance>
<DataInstance>
<instanceId>Movie</instanceId>
<field name='title'>Casablanca</field>
<field name='director'>Michael Curtiz</field>
<field name='genre'>Drama</field>
</DataInstance>
</DataInstances>
</DataObjects>
Each InstanceId has a single entry, with unique field names, and I'm trying to get create a table output for each unique InstanceId, so a "Cd" table, a "Movie" table, and so on. Each field would be a new row, the first column would be what is in name=, and the value would be in the second column of the row. I think once I figure out how to grab the value from name="" and put it as a row name, I'll be able to figure out the rest. All the tutorials talk about using value-of, but that only works if the format is like the first example given.
An example XSLT that I'm using as a starting point. It does not like the field name= format.
<?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>
<h2>Catalog Selection A1</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Name</th>
<th>Value</th>
</tr>
<xsl:for-each select="DataObjects/DataInstances/DataInstance/instanceId/Cd">
<tr>
<td>Title</td>
<td><xsl:value-of select="title" /></td>
</tr>
<tr>
<td>Artist</td>
<td><xsl:value-of select="artist" /></td>
</tr>
<tr>
<td>Country</td>
<td><xsl:value-of select="country" /></td>
</tr>
<tr>
<td>Company</td>
<td><xsl:value-of select="company" /></td>
</tr>
</xsl:for-each>
<xsl:for-each select="DataObjects/DataInstances/DataInstance/instanceId/Movie">
<tr>
<td>Title</td>
<td><xsl:value-of select="title" /></td>
</tr>
<tr>
<td>Director</td>
<td><xsl:value-of select="director" /></td>
</tr>
<tr>
<td>Genre</td>
<td><xsl:value-of select="genre" /></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
The output I'm trying to generate is a basic html table like this,
<html>
<body>
<h2>Catalog Selection A1</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Name</th>
<th>Value</th>
</tr>
<tr>
<td>Title</td>
<td>Empire Burlesque</td>
</tr>
<tr>
<td>Artist</td>
<td>Bob Dylan</td>
</tr>
<tr>
<td>Country</td>
<td>USA</td>
</tr>
<tr>
<td>Company</td>
<td>Columbia</td>
</tr>
</table>
<table border="1">
<tr bgcolor="#9acd32">
<th>Name</th>
<th>Value</th>
</tr>
<tr>
<td>Title</td>
<td>Casablanca</td>
</tr>
<tr>
<td>Director</td>
<td>Michael Curtiz</td>
</tr>
<tr>
<td>Genre</td>
<td>Drama</td>
</tr>
</table>
</body>
</html>
CodePudding user response:
I would suggest you do something along the lines of:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/DataObjects">
<html>
<body>
<h2>
<xsl:value-of select="ObjectSelect/meNm" />
</h2>
<h3>Cds</h3>
<xsl:for-each select="DataInstances/DataInstance[instanceId='Cd']">
<xsl:call-template name="table"/>
</xsl:for-each>
<h3>Movies</h3>
<xsl:for-each select="DataInstances/DataInstance[instanceId='Movie']">
<xsl:call-template name="table"/>
</xsl:for-each>
</body>
</html>
</xsl:template>
<xsl:template name="table">
<table border="1">
<tr>
<th>Name</th>
<th>Value</th>
</tr>
<xsl:for-each select="field">
<tr>
<td>
<xsl:value-of select="@name" />
</td>
<td>
<xsl:value-of select="." />
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
This minimizes code duplication by (1) using a named template to produce a table for any kind of instanceId
and (2) processing each field
to a row with name and value. However this also means that it will produce a row for every field
in the input. If that's not what you want, then more work is required.
Added:
If you only have one of each kind, and you don't mind preserving the original order, then you can do simply:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/DataObjects">
<html>
<body>
<h2>
<xsl:value-of select="ObjectSelect/meNm" />
</h2>
<xsl:for-each select="DataInstances/DataInstance">
<table border="1">
<tr>
<th>Name</th>
<th>Value</th>
</tr>
<xsl:for-each select="field">
<tr>
<td>
<xsl:value-of select="@name" />
</td>
<td>
<xsl:value-of select="." />
</td>
</tr>
</xsl:for-each>
</table>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>