tags:

views:

51

answers:

3

I have some XML files of this form:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="biomirror.xsl"?>
<Thread>
    <Title> Some thread title </Title>
    <Posts>
        <Post>
            <Author> Me </Author>
            <Body>
                This is the post body, which <b>may</b> have embedded XHTML, including all sorts of things like:<br />
                <div class="quote">Quotes</div>
                I know it's XHTML, though, the program spitting out XML verifies that.
            </Body>
        </Post>
    </Posts>
</Thread>

I need to format them into readable threads, so I'm using a CSS stylesheet and an XSL stylesheet. The CSS works, I know for a fact there's nothing wrong with that. My problem seems to be with the XSL, as any embedded XHTML isn't being parsed by Firefox. In IE it works perfectly and comes out with proper formatting, but in Firefox it's entirely plain-text. I assume that has to do with it being escaped before being output, but I can't figure out how to prevent that.

XSL is:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
<xsl:output 
  omit-xml-declaration="yes" 
  method="xml" 
  media-type="application/xhtml+xml" 
  indent="no" 
  doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
/>
<xsl:template match="Posts">
<xsl:for-each select="Post">
    <tr xmlns="http://www.w3.org/1999/xhtml" class="Post">
      <td>
        <div>
          <table width="100%" cellpadding="0" cellspacing="0">
            <tr>
              <td class="Author">
                <xsl:value-of select="Author"/>
              </td>
              <td class="Date">
                Post <xsl:value-of select="PostID"/> 
                <xsl:choose>
                  <xsl:when test="count(LastPost) > 0">
                    (lastpost)
                  </xsl:when>
                </xsl:choose> at <xsl:value-of select="Date"/>
              </td>
            </tr>
          </table>
        </div>
        <div class="Body">
           <xsl:copy-of select="Body" />
        </div>
        <xsl:choose>
          <xsl:when test="count(Sig) = 1">
            <div class="Sig">
              <xsl:value-of disable-output-escaping="yes" select="Sig"/>
            </div>
          </xsl:when>
          <xsl:when test="count(Sig) = 0">
            <div class="SigFooter"> </div>
          </xsl:when>
        </xsl:choose>
      </td>
    </tr>
</xsl:for-each>
</xsl:template>

<xsl:template match="Thread">
  <html xmlns="http://www.w3.org/1999/xhtml"&gt;
    <head>
        <xsl:choose>
          <xsl:when test="count(Title) = 1">
            <title>
              <xsl:value-of select="Title"/>
            </title>
          </xsl:when>
        </xsl:choose>
        <link href="resources/main.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
      <table class="Thread" align="center" width="90%" height="95%" cellpadding="2em">
        <tr>
          <td colspan="3">
            <div class="Title">
          <xsl:value-of select="Title"/>
              <br />
              <a href="whatis.xml">
                <img src="resources/banner.png" />
              </a>
            </div>
          </td>
        </tr>
        <xsl:apply-templates select="Posts"/>
        <tr height="100%">
         <td valign="bottom">
       <div class="Footer">
             Footer message n stuff
           </div>
         </td>
        </tr>
      </table>
    </body>
  </html>
</xsl:template>

It's a bit of a hack and any odd attributes I blame on Visual Studio. This is my first time messing around with XSL (relatively familiar with XML, though), so I haven't the foggiest what to do to fix this. :)

Now, I read this question: http://stackoverflow.com/questions/1105176/xslt-parsing-html-embedded-in-xml and tried to integrate that into my XML and XSL (as shown above). Still doesn't work in Firefox, though.

Edit: Also, I tried both xsl:value-of and xsl:copy-of to output the content. Value-of outputs plain-text and respects my formatting (from the CSS), copy-of outputs plain-text and ruins my formatting (drops back to body formatting, disregarding divs and table).

Edit2: Revised XSL to reflect suggestions from answers. Formatting is fine, but the embedded tags are still coming out as text, not being interpreted.

+1  A: 

First, you don't need these declarations:

xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"

it's safe to remove them.

Second, you never need to mention the child:: axis. This is implicit in XSLT, just kick it out without replacement.

Third, move the XHTML namespace declaration

xmlns="http://www.w3.org/1999/xhtml"

to the <xsl:stylesheet> element and remove it everywhere else.

Last but not least: Have you tried the correct output method and media type for XHTML?

<xsl:output method="xml" media-type="application/xhtml+xml">
Tomalak
Oh, and also: CSS is case-sensitive in standards rendering mode. Just double check that your class names are correct.
Tomalak
My class names are correct, it looks like. I just tried all your suggestions. It fixed the problem with copy-to ignoring styles, but introduced a thin white border around the page and still didn't fix the XHTML not being interpreted as such.
peachykeen
@peachy: These were just suggestions. You do client-side XSLT processing?
Tomalak
I assume so. I'm not familiar with XSLT, like I said. I have the XML pages, including the xml-stylesheet. Those get opened in the browser, so as far as I can tell, it's up to the browser to transform it. Hence why it works in IE but not Firefox. I could do it server-side, but I don't know how many pages or what content they'll have, or when they'll update.
peachykeen
+1  A: 

First: I'm running this with Firefox 3.5.9. It has no problem, but that is wrong.

When you say:

<xsl:copy-of select="child::Body" /> 

You are coping a non namespace element with its content. And because you didn't declare this content with XHTML namespace, these are not XHTML elements. They should have a xmlns="" declaration. But my Firefox version is doing things wrong: interpret no namespace element (as example b) as XHTML elements (without empty namespace!). Because you didn't provide CSS stylesheet I can't do CSS test (if no namespace elements get the style).

Edit: MSXSL adds correctly xmlns="" declaration in Body, but then IE renders whatever MS wants. JA!

Alejandro
I just edited the post to reflect the suggestions from the other answer. The styles are fine, I think all the stylesheet issues are fixed. Either way, I can debug CSS on my own no problem, I'm just not familiar with XSL. I removed the child:: parts. In Firefox 3.6.6 it doesn't work, though. What do you mean declare the contents with XHTML namespace?
peachykeen
@peachykeen: You need to add a default namespace declaration to your `Body` element in input as `xmlns="http://www.w3.org/1999/xhtml"`, also define a prefix with that namespace in the stylesheet as `xmlns:xhtml="http://www.w3.org/1999/xhtml"` and select the `Body` element content with `xhtml:Body/node()` xpath.
Alejandro
Just tried that, still no parsing of the embedded XHTML in FF 3.6.6. Will keep trying in case I misunderstood you, though.
peachykeen
@Alejandro: I tried playing with this more, and was able to retrieve the nodes using your example. However, on my FF, it doesn't fix the situation, and actually breaks it on IE also. I'm not sure why we're getting such different results. Is there any chance you could edit your answer to include the code as you suggest, to make sure I'm understanding correctly?
peachykeen
A: 

Your XML input does not have XHTML embedded as that would require those elements you want to be recognized as XHTML to be in the XHTML namespace http://www.w3.org/1999/xhtml. So you either need to change your input to put those elements in the XHTML namespace, then you can indeed simply copy them in the stylesheet to the result tree, or you need to change your stylesheet to transform those elements to ones in the XHTML namespace. The following does that, also making some other changes like setting version="1.0":

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns="http://www.w3.org/1999/xhtml"&gt;
<xsl:output 
  omit-xml-declaration="yes" 
  method="xml" 
  media-type="application/xhtml+xml" 
  indent="no" 
  doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
/>
<xsl:template match="Posts">
<xsl:for-each select="Post">
    <tr class="Post">
      <td>
        <div>
          <table width="100%" cellpadding="0" cellspacing="0">
            <tr>
              <td class="Author">
                <xsl:value-of select="Author"/>
              </td>
              <td class="Date">
                Post <xsl:value-of select="PostID"/> 
                <xsl:choose>
                  <xsl:when test="count(LastPost) > 0">
                    (lastpost)
                  </xsl:when>
                </xsl:choose> at <xsl:value-of select="Date"/>
              </td>
            </tr>
          </table>
        </div>
        <div class="Body">
           <xsl:apply-templates mode="to-xhtml" />
        </div>
        <xsl:choose>
          <xsl:when test="count(Sig) = 1">
            <div class="Sig">
              <xsl:value-of disable-output-escaping="yes" select="Sig"/>
            </div>
          </xsl:when>
          <xsl:when test="count(Sig) = 0">
            <div class="SigFooter"> </div>
          </xsl:when>
        </xsl:choose>
      </td>
    </tr>
</xsl:for-each>
</xsl:template>

<xsl:template match="Thread">
  <html>
    <head>
        <xsl:choose>
          <xsl:when test="count(Title) = 1">
            <title>
              <xsl:value-of select="Title"/>
            </title>
          </xsl:when>
        </xsl:choose>
        <link href="resources/main.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
      <table class="Thread" align="center" width="90%" height="95%" cellpadding="2em">
        <tr>
          <td colspan="3">
            <div class="Title">
          <xsl:value-of select="Title"/>
              <br />
              <a href="whatis.xml">
                <img src="resources/banner.png" />
              </a>
            </div>
          </td>
        </tr>
        <xsl:apply-templates select="Posts"/>
        <tr height="100%">
         <td valign="bottom">
       <div class="Footer">
             Footer message n stuff
           </div>
         </td>
        </tr>
      </table>
    </body>
  </html>
 </xsl:template>

  <xsl:template match="*" mode="to-xhtml">
    <xsl:element name="{local-name()}">
      <xsl:apply-templates select="@* | node()" mode="to-xhtml"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="@* | text() | processing-instruction() | comment()" mode="to-xhtml">
    <xsl:copy/>
  </xsl:template>

</xsl:stylesheet>
Martin Honnen