ssube
ssube

Reputation: 48267

Convince Firefox to parse HTML embedded in XML (after XSL is applied)

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">
<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">
    <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: 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.

Upvotes: 2

Views: 2457

Answers (3)

Martin Honnen
Martin Honnen

Reputation: 167506

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">
<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>

Upvotes: 0

Tomalak
Tomalak

Reputation: 338208

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">

Upvotes: 2

user357812
user357812

Reputation:

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!

Upvotes: 1

Related Questions