views:

2722

answers:

7

I'm using xslt to transform xml to an aspx file. In the xslt, I have a script tag to include a jquery.js file. To get it to work with IE, the script tag must have an explicit closing tag. For some reason, this doesn't work with xslt below.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
    xmlns:asp="remove">    
    <xsl:output method="html"/>
    <xsl:template match="/">
        <html xmlns="http://www.w3.org/1999/xhtml"&gt;
            <head>
                <title>TEST</title>
                <script type="text/javascript" src="jquery-1.2.6.js"></script>

But if I change the script tag as shown below, it works.

        <script type="text/javascript" src="jquery-1.2.6.js">
            // <![CDATA[ // ]]>
        </script>

I thought that the <xsl:output method="html" /> would do the trick, but it doesn't seem to work?

/Jonas

A: 

And this is against MS's XSLT engine of course? How strange. I can't say I've encountered this in .NET 1.1 or 2.0 and I have a number of transforms doing exactly the same thing, so I'd suggest you go with the simple workaround.

annakata
A: 

Yes, it's the .NET 2 xsl compiled transformer. I think I'll use the workaround ....

+2  A: 

This is quite a common issue when generating HTML from XSLT. I suspect the root of the problem is that the XSLT itself is an XML document. Even though you start with your XSLT programme as serialized XML, by the time the XSLT processor gets it, it's already been de-serialized to an "infoset" representation. At an XML level, an empty tag and a self-closed one are equivalent; the element quite simply doesn't have children. This means that your XSLT processor will probably never see a version of the programme where the script element has both an opening and closing tag.

I usually get round this by using a suitably escaped non-breaking space. In any case, you've got to put something inside the script tag.

By the way - this isn't about making IE happy. If you look at the HTML spec, you'll find that the end tag is required. I'm sure some people will find it ironic that IE implements it correctly. :-)

Dominic Cronin
+4  A: 

If you're creating the XmlWriter yourself you need to pass the transform's OutputSettings to the XmlWriter, eg:

XmlDocument doc = new XmlDocument();
doc.LoadXml("<book><author>Trudi Canavan</author><title>Voice of the Gods</title></book>");

XslCompiledTransform transform = new XslCompiledTransform();
transform.Load("XSLTFile1.xslt");

StringBuilder output = new StringBuilder();

// Here we pass the output setting to the writer, otherwise the transform
// may be set to Html, but the XmlWriter will be outputting Xml
XmlWriter writer = XmlWriter.Create(output, transform.OutputSettings);

transform.Transform(doc, writer);

Console.WriteLine(output.ToString());
Console.ReadKey();
KeeperOfTheSoul
A: 

Thanks for your post.. I used a comment before script end tag to go through this issue.

Regards, Kapil

Kapil
A: 

This is by design and the only workaround I know of is to use a custom XmlTextWriter :

public class HtmlTextWriter : XmlTextWriter
{
    private readonly string[] fullEndElements = 
        new string[] { "script", "title" };

    private string lastStartElement = null;


    public HtmlTextWriter(TextWriter textWriter)
        : base(textWriter)
    {
    }

    public HtmlTextWriter(string filename, Encoding encoding)
        : base(filename, encoding)
    {
    }

    public HtmlTextWriter(Stream w, Encoding encoding)
        : base(stream, encoding)
    {
    }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        lastStartElement = localName;
        base.WriteStartElement(prefix, localName, ns);
    }

    public override void WriteEndElement()
    {
        if (Array.IndexOf(fullEndElements, lastStartElement) > -1)
        {
            base.WriteFullEndElement();
        }
        else
        {
            base.WriteEndElement();
        }
    }
}
Diadistis
A: 

For those of you using Xalan 2.7.0 (in Java), I had the same problem. I adopted the org/apache/xml/serializer/ToStream.java file in the original source code to work around this problem. If you specify as outputProperty:

private static Properties XHTML() {
  Properties p = new Properties();
  p.setProperty(OutputKeys.METHOD, "xhtml");
  p.setProperty(OutputKeys.DOCTYPE_PUBLIC,
    "-//W3C//DTD XHTML 1.0 Strict//EN");
  p.setProperty(OutputKeys.DOCTYPE_SYSTEM,
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
  p.setProperty(OutputKeys.ENCODING, "UTF-8");
  p.setProperty("{http://xml.apache.org/xalan}indent-amount", "4");
  p.setProperty(OutputKeys.INDENT, "yes");
  p.setProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
  p.setProperty("{http://example.com}selfclosing","no");
  return p;
 }

Note the last line on selfclosing. This is a property that is responded to in this renewed version of ToStream.java and will prevent <div /> and <script /> tags and output <div></div> and <script></script> tags instead. It also includes exceptions to these, namely the ones specified in the HTML standard as allowed to be self-closing (i.e. "area", "base", "basefont", "br", "col", "frame", "hr", "img", "input", "link", "meta", "param"). It worked for me at least, and now webpages are rendered fine in IE as well.

The new ToStream.java file can be found at http://bvandelft.ruhosting.nl/ToStream.java and a pre-compiled version of the serializer.jar (working with 2.7) is also available at the same url but serializer.jar (in which the compiled version of ToStream is included) - wasn't allowed to post over 1 URL :-p

Bart van Delft
I am aware that you can add a comment in the script-tag to hack around this as well, however this will not solve the problem of having e.g. empty divs, hence this larger-scale solution.
Bart van Delft