tags:

views:

84

answers:

4

i want to transform some xml into HTML that has the following format:

<TR><TD> col1 <TD> col2 <TD> col3 </TR>

Note: The output is HTML, complete with optional closing tags omitted. This is the problem, and the reason the question exists.

A snippet of the XSL i'm using is:

<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output 
    doctype-system='http://www.w3.org/TR/html4/strict.dtd'
    doctype-public='-//W3C//DTD HTML 4.01//EN'
    indent='yes'
    method='html'
    />
   ...
   <xsl:for-each select="/">  
      <TR><TD><xsl:value-of select="col1"/><TD><xsl:value-of select="col2"/><TD><xsl:value-of select="col3"/></TR>
   </xsl:for-each>

You can see that the guts of the XSL matches my desired HTML (wrapped for easy reading):

<TR>  <TD><xsl:value-of select="Column1"/>
      <TD><xsl:value-of select="Column2"/>
      <TD><xsl:value-of select="Column3"/> </TR>

Note: Those of you who know the error i'm getting from the XSLT: hopefully already know the answer.

When presented with my XSL (which, don't forget, is a form of xml), i get the non-well formed error:

End tag 'TR' does not match the start tag 'TD'.

This makes perfect sense. Indeed:

<TD><xsl:value-of select="Column3"/> </TR>

i do not close the TD element before closing the TR. So the question is:

How can i transform xml into HTML, given that HTML is not xml?

See also


Update one

It has been suggested that one could simply include the closing tags anyway, in order to make the XSL validate (shown wrapped for easy reading):

<TR>    <TD><xsl:value-of select="col1"/></TD>
        <TD><xsl:value-of select="col2"/></TD>
        <TD><xsl:value-of select="col3"/></TD>   </TR>

then, by using xsl:output method='html', the final HTML content would have the </TD> tags magically omitted. Except it doesn't work:

<TR><TD>col1</TD><TD>col2</TD><TD>col3</TD></TR>

Update two

It has been suggested that i give up, don't bother asking this question, and just include the optional closing tags. That's possible, but that's not my question. Also, the "solution" doesn't work for elements where the closing tag is forbidden, e.g.:

<BR/>

or

<BR></BR>

How would i include a <BR> element in my HTML output, given that it is forbidden in HTML to close a <BR> element.

A: 

Ian, have you tried <xsl:output method="text">?

http://www.w3schools.com/xsl/el_output.asp

code4life
Yes, the source parser complains if it's not given valid XML - this seems to be a fundamental constraint. The source XSL (which itself *is* xml) must be valid XML. And as soon as you begin adding < and > symbols, it assume they're the start of element names.
Ian Boyd
+1  A: 

Have you tried the output as "HTML"? Elements that shouldn't be self-closed in HTML are not (i.e. <BR>, <img>).

If you still don't like how the XSLT engine is serializing HTML output, then you could set your <xsl:output method="text"> and construct the "HTML" that you want:

&lt;TR>&lt;TD><xsl:value-of select="col1"/>&lt;TD><xsl:value-of select="col2"/>&lt;TD><xsl:value-of select="col3"/>&lt;/TR>

which produces:

<TR><TD>col1<TD>col2<TD>col3</TR>  
Mads Hansen
Oh good lord. That's much worse than it not generating the `HTML` i want...the `XSL` has to be something *nobody* would want!
Ian Boyd
i see the problem now. i was looking at XSLT as a transformation engine, that could take the source XML and turn it into whatever i wanted, filling in bits like a mail-merge. This is not the case: the **xslt** you give it must be valid **xml** (because xslt *is* xml). i can't go in there willy-nilly adding HTML markup - the parser doesn't know the difference between an `html` element and an `xml` element. XSLT is not as flexible as a mail-merge, and i cannot treat it as such. In the end, i need to bend my requirements to the limitations of the transformation engine.
Ian Boyd
As much as i don't *like* the answer, it is the corret answer. Your answer cannot change the limitations of XSLT; only help to give the best possible workarounds. Accepted.
Ian Boyd
@Ian-Boyd: There are no "limitations of XSLT" -- you can produce *exactly* the output you wanted -- see for example my answer.The mail/merge solution is also possible -- even if the form isn't well-formed XML -- one could just process it as text. This is even easier with XSLT 2.0 and XPath 2.0 with the powerful RegEx capabilities they provide.
Dimitre Novatchev
+2  A: 

I believe the simplest thing is to just accept you're going to have closing tags in the output. While they might be optional, I believe most people would agree that best practice is to include them.

Is there a reason you really don't want optional closing tags in the output?

Re Update Two

There's no problem with this update. With method="html" <BR/> will be output as <BR>:

XSLT (note <BR/>):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
<xsl:output 
doctype-system='http://www.w3.org/TR/html4/strict.dtd'
doctype-public='-//W3C//DTD HTML 4.01//EN'
indent='yes'
method='html'
/>

<xsl:template match="/">
<HTML><BODY>
    <TR>
        <xsl:apply-templates/>
    </TR>
    <BR/> <!-- HERE -->
</BODY></HTML>
</xsl:template>

<xsl:template match="item">
    <TD><xsl:value-of select="."/></TD>
</xsl:template>

</xsl:stylesheet>

Input:

<root>
<item>one</item>
<item>two</item>
</root>

Output (note <BR>):

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"&gt;
<HTML><BODY>
<TR>
<TD>one</TD>
<TD>two</TD>
</TR>
<BR> <!-- HERE -->
</BODY></HTML>
Porges
You are correct; as long as the output type is `html`. Which is what i have. But then that discounts other answers that require output `text`. (That's why i included it as an answer, as a competing goal)
Ian Boyd
+1  A: 

Here's one way to do this:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output method="text"/>

 <xsl:template match="/*">
   &lt;TR>&lt;TD><xsl:value-of select="col1"/>&lt;TD><xsl:value-of select="col2"/>&lt;TD><xsl:value-of select="col3"/>&lt;/TR>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document:

<t>
 <col1>1</col1>
 <col2>2</col2>
 <col3>3</col3>
</t>

the wanted result is correctly produced:

   <TR><TD>1<TD>2<TD>3</TR>
Dimitre Novatchev