For a given xml, I need to generate a html table to represent the values in the xml. I need the recursion for any keyN, if valueN is text then just print it. If valueN is xml, then print a (nested)table with it's value. I think my lack of understanding of how to use XSLT recursion correctly is the base of the question. Any help appreciated.
Input:
<root>
<key1> Text Value </key1>
<key2>
<a> aaa </a>
<b> bbb </b>
</key2>
<keyN> valueN </keyN>
<root>
Output:
<table border="1px">
<tr>
<td> key1 </td>
<td> Text Value </td>
</tr>
<tr>
<td> key2 </td>
<td>
<table border="1px">
<tr> <td> a </td> <td> aaa </td> </tr>
<tr> <td> b </td> <td> bbb </td> </tr>
</table>
</td>
</tr>
<tr>
<td> keyN </td>
<td>
valueN (if valueN is text)
OR
<table> ... </table> (if valueN is xml)
<td>
</tr>
</table>
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/*//*[1]">
<table border="1">
<xsl:call-template name="makeRow"/>
</table>
</xsl:template>
<xsl:template match="*" name="makeRow">
<tr>
<td>
<xsl:value-of select="name()"/>
</td>
<td>
<xsl:apply-templates select="node()[1]"/>
</td>
</tr>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match="/*">
<xsl:apply-templates select="node()[1]"/>
</xsl:template>
</xsl:stylesheet>
Output:
<table border="1">
<tr>
<td>key1</td>
<td> Text Value </td>
</tr>
<tr>
<td>key2</td>
<td>
<table border="1">
<tr>
<td>a</td>
<td> aaa </td>
</tr>
<tr>
<td>b</td>
<td> bbb </td>
</tr>
</table></td>
</tr>
<tr>
<td>keyN</td>
<td> valueN </td>
</tr>
</table>
Note: This use the fine grained traversal patter. Three rules: "first child descendant of root element", output table and call makeRow; makeRow (match any element not being the first child descendant nor root element) output tr and table cells with name and first child applycation, then apply templates to next sibling; root element rule, start the fine grained traversal.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<table border="1px">
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match="*[*][parent::*]">
<tr>
<td><xsl:value-of select="name()"/></td>
<td>
<table border="1px">
<xsl:apply-templates/>
</table>
</td>
</tr>
</xsl:template>
<xsl:template match="*[not(*)]">
<tr>
<td><xsl:value-of select="name()"/></td>
<td><xsl:value-of select="."/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<root>
<key1> Text Value </key1>
<key2>
<a> aaa </a>
<b> bbb </b>
</key2>
<keyN> valueN </keyN>
</root>
produces the wanted, correct result:
<table border="1px">
<tr>
<td>key1</td>
<td> Text Value </td>
</tr>
<tr>
<td>key2</td>
<td>
<table border="1px">
<tr>
<td>a</td>
<td> aaa </td>
</tr>
<tr>
<td>b</td>
<td> bbb </td>
</tr>
</table>
</td>
</tr>
<tr>
<td>keyN</td>
<td> valueN </td>
</tr>
</table>
Do note the power of XSLT:
No explicit recursion.
No conditionals inside any template.
Completely push-style of processing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With