Do I have a bug, or am I pushing the limits of XSLT engines?

Mike Whittemore

Usenet Archive
Joined
May 3, 2004
Messages
3
Reaction score
0
I am trying to convert an HTML table into a list of name-value pairs, one pair per field in the table. I believe my XSLT is correct, but I've tried both Xalan and Saxon, which both fail with different results. Below I've listed my HTML input, my expected XML output, my XSLT, and the actual output from both Saxon and Xalan. Thanks in advance.

HTML Input
<html>
<head/>
<body>
<table>
<tr>
<th>Field 1</th>
<th>Field 2</th>
</tr>
<tr>
<td>A</td>
<td>1</td>
</tr>
<tr>
<td>B</td>
<td>2</td>
</tr>
</table>
</body>
</html>

Expected XML Output
<?xml version="1.0" encoding="UTF-8" ?>
<records>
<record>
<field>
<name>Field 1</name>
<value>A</value>
</field>
<field>
<name>Field 2</name>
<value>1</value>
</field>
</record>
<record>
<field>
<name>Field 1</name>
<value>B</value>
</field>
<field>
<name>Field 2</name>
<value>2</value>
</field>
</record>
</records>

XSLT
----
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:variable name="fields" select="//th" />

<xsl:template name="lookup-field">
<xsl:param name="at-index"/>
<xsl:value-of select="$fields[$at-index]" />
</xsl:template>

<xsl:template match="/html/body/table">
<records>
<xsl:apply-templates select="tr[td]"/>
</records>
</xsl:template>

<xsl:template match="tr[td]">
<record>
<xsl:for-each select="td">
<field>
<name>
<xsl:call-template name="lookup-field">
<xsl:with-param name="at-index">
<xsl:value-of select="position"/>
</xsl:with-param>
</xsl:call-template>
</name>
<value>
<xsl:value-of select="."/>
</value>
</field>
</xsl:for-each>
</record>
</xsl:template>

</xsl:transform>

Actual Output From Saxon:
<?xml version="1.0" encoding="UTF-8" ?>
<records>
<record>
<field>
<name>Field 1</name>
<value>A</value>
</field>
<field>
<name>Field 1</name>
<value>1</value>
</field>
</record>
<record>
<field>
<name>Field 1</name>
<value>B</value>
</field>
<field>
<name>Field 1</name>
<value>2</value>
</field>
</record>
</records>

Actual Output From Xalan:
 

A. Bolmarcich

Usenet Archive
Joined
May 9, 2002
Messages
219
Reaction score
0
I am trying to convert an HTML table into a list of name-value pairs, one pair per field in the table. I believe my XSLT is correct, but I've tried both Xalan and Saxon, which both fail with different results. Below I've listed my HTML input, my expected XML output, my XSLT, and the actual output from both Saxon and Xalan. Thanks in advance.

[snipped most of helpful example]
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:variable name="fields" select="//th" />

<xsl:template name="lookup-field">
<xsl:param name="at-index"/>
<xsl:value-of select="$fields[$at-index]" />
</xsl:template>

<xsl:template match="/html/body/table">
<records>
<xsl:apply-templates select="tr[td]"/>
</records>
</xsl:template>

<xsl:template match="tr[td]">
<record>
<xsl:for-each select="td">
<field>
<name>
<xsl:call-template name="lookup-field">
<xsl:with-param name="at-index">
<xsl:value-of select="position"/>
</xsl:with-param>

Replace the above three lines with

<xsl:with-param name="at-index" select="position"/>

What you have sets the param to a result tree fragment. You need to set the param to a number, so that in the called template $at-index evaluates to a number.
 

Mike Whittemore

Usenet Archive
Joined
May 3, 2004
Messages
3
Reaction score
0
I am trying to convert an HTML table into a list of name-value pairs, one pair per field in the table. I believe my XSLT is correct, but I've tried both Xalan and Saxon, which both fail with different results. Below I've listed my HTML input, my expected XML output, my XSLT, and the actual output from both Saxon and Xalan. Thanks in advance.

[snipped most of helpful example]
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:variable name="fields" select="//th" />

<xsl:template name="lookup-field">
<xsl:param name="at-index"/>
<xsl:value-of select="$fields[$at-index]" />
</xsl:template>

<xsl:template match="/html/body/table">
<records>
<xsl:apply-templates select="tr[td]"/>
</records>
</xsl:template>

<xsl:template match="tr[td]">
<record>
<xsl:for-each select="td">
<field>
<name>
<xsl:call-template name="lookup-field">
<xsl:with-param name="at-index">
<xsl:value-of select="position"/>
</xsl:with-param>

Replace the above three lines with

<xsl:with-param name="at-index" select="position"/>

What you have sets the param to a result tree fragment. You need to set the param to a number, so that in the called template $at-index evaluates to a number.
</xsl:call-template>
</name>
<value>
<xsl:value-of select="."/>
</value>
</field>
</xsl:for-each>
</record>
</xsl:template>

</xsl:transform>

Thanks for your response. That worked perfectly for the Saxon and Microsoft engines, but I still get a null pointer exception from the Xalan engine. I suppose this show's it is a good idea to test your XSL transformations on multiple engines if you want them to increase the chances of them being portable. Below I've posted a different solution that works for all 3 engines mentioned. I'm still new at this, so if you spot a more elegant solution I'd appreciate seeing it. Thanks again.

"Portable" XSLT Solution:
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/html/body/table">
<records>
<xsl:apply-templates select="tr[td]"/>
</records>
</xsl:template>

<xsl:template match="tr[td]">
<record>
<xsl:for-each select="td">
<field>
<name>
<xsl:variable name="pos" select="position"
/>
<xsl:value-of select="/descendant::th[$pos]"/>
</name>
<value>
<xsl:value-of select="."/>
</value>
</field>
</xsl:for-each>
</record>
</xsl:template>

</xsl:transform>

PS. I'm not sure why I need to assign position to a variable first, but without doing it this way it was always equating to "1".
 

Mike Whittemore

Usenet Archive
Joined
May 3, 2004
Messages
3
Reaction score
0
I am trying to convert an HTML table into a list of name-value pairs, one pair per field in the table. I believe my XSLT is correct, but I've tried both Xalan and Saxon, which both fail with different results. Below I've listed my HTML input, my expected XML output, my XSLT, and the actual output from both Saxon and Xalan. Thanks in advance.

[snipped most of helpful example]
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:variable name="fields" select="//th" />

<xsl:template name="lookup-field">
<xsl:param name="at-index"/>
<xsl:value-of select="$fields[$at-index]" />
</xsl:template>

<xsl:template match="/html/body/table">
<records>
<xsl:apply-templates select="tr[td]"/>
</records>
</xsl:template>

<xsl:template match="tr[td]">
<record>
<xsl:for-each select="td">
<field>
<name>
<xsl:call-template name="lookup-field">
<xsl:with-param name="at-index">
<xsl:value-of select="position"/>
</xsl:with-param>

Replace the above three lines with

<xsl:with-param name="at-index" select="position"/>

What you have sets the param to a result tree fragment. You need to set the param to a number, so that in the called template $at-index evaluates to a number.
</xsl:call-template>
</name>
<value>
<xsl:value-of select="."/>
</value>
</field>
</xsl:for-each>
</record>
</xsl:template>

</xsl:transform>

Thanks for your response. That worked perfectly for the Saxon and Microsoft engines, but I still get a null pointer exception from the Xalan engine. I suppose this show's it is a good idea to test your XSL transformations on multiple engines if you want them to increase the chances of them being portable. Below I've posted a different solution that works for all 3 engines mentioned. I'm still new at this, so if you spot a more elegant solution I'd appreciate seeing it. Thanks again.

"Portable" XSLT Solution:
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/html/body/table">
<records>
<xsl:apply-templates select="tr[td]"/>
</records>
</xsl:template>

<xsl:template match="tr[td]">
<record>
<xsl:for-each select="td">
<field>
<name>
<xsl:variable name="pos" select="position"
/>
<xsl:value-of select="/descendant::th[$pos]"/>
</name>
<value>
<xsl:value-of select="."/>
</value>
</field>
</xsl:for-each>
</record>
</xsl:template>

</xsl:transform>

PS. I'm not sure why I need to assign position to a variable first, but without doing it this way it was always equating to "1".

My last post on this. I switched from using the version of Xalan (2.2) that shipped with my JVM, to the latest 2.6. After reading the Xalan FAQ to figure out how to override the default version of Xalan, I ran my test and it succeeded. Thanks again for the help.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
548,208
Messages
3,104,802
Members
249,594
Latest member
henrywalsh

Latest Threads

Top