XSLT self-closing tags issue
Asked Answered
R

12

26

I am using xslt to transform an xml file to html. The .net xslt engine keeps serving me self-closing tags for empty tags.

Example:

<div class="test"></div> 

becomes

<div class="test" />

The former is valid html, while the latter is illegal html and renders badly. My question is : How do I tell the xslt engine (XslCompiledTransform) to not use self-closing tags.

If it's not possible, how can I tell my browser (IE6+ in this case) to interpret self-closing tags correctly.

Rosenarosenbaum answered 20/5, 2009 at 12:55 Comment(1)
This article really helped me understand the issue webkit.org/blog/68/understanding-html-xml-and-xhtmlDelude
K
12

If you are using XmlWriter as your ouput stream, use HTMLTextWriter instead. XMLWriter will reformat your HTML output back to XML.

Kristinakristine answered 20/5, 2009 at 13:54 Comment(3)
the problem is that TextWriter is abstract.Rosenarosenbaum
Ok, I just found about HtmlTextWriter (from ASP.net - System.Web.UI). I think it holds the solution. TY.Rosenarosenbaum
the easy I found was creating a new XmlTextWriter class and override the method WriteEndElement, follows the complete answer https://mcmap.net/q/514310/-xslt-self-closing-tags-issueConsequence
V
25

Change your xsl:output method to be html (instead of xml).

Or add it if you haven't already got the element

<xsl:output method="html"/>
Varnado answered 20/5, 2009 at 12:59 Comment(2)
It dosen't seem to work. I've tried various combinaison of <xsl:output ...> but he keeps giving me xml.Rosenarosenbaum
Do you have two <xsl:output> entries by any chance? I'm not using .NET, but Xalan in Java allows thatVarnado
P
18

A workaround can be to insert a comment element to force generation of non self closing:

<script type="text/javascript" src="nowhere.js">
<xsl:comment></xsl:comment>
</script>

It is not a pretty soloution, but it works :-)

/Sten

Parquetry answered 23/1, 2012 at 12:17 Comment(2)
This solution is perfect for me. ThanksMetallic
Pretty like my mom! Thanks for that!Cline
K
12

If you are using XmlWriter as your ouput stream, use HTMLTextWriter instead. XMLWriter will reformat your HTML output back to XML.

Kristinakristine answered 20/5, 2009 at 13:54 Comment(3)
the problem is that TextWriter is abstract.Rosenarosenbaum
Ok, I just found about HtmlTextWriter (from ASP.net - System.Web.UI). I think it holds the solution. TY.Rosenarosenbaum
the easy I found was creating a new XmlTextWriter class and override the method WriteEndElement, follows the complete answer https://mcmap.net/q/514310/-xslt-self-closing-tags-issueConsequence
P
4

This is related to the XslCompiledTransform class

here is a workaround:

http://blogs.msdn.com/b/nareshjoshi/archive/2009/01/15/how-to-force-non-self-closing-tags-for-empty-nodes-when-using-xslcompiledtransform-class.aspx

Penta answered 15/9, 2010 at 15:53 Comment(1)
Be aware that the code does opposite of what it says. You should take the code in the comment.Marietta
G
3

For me it was a problem in the script tag. I solved it by filling it with a semicolon (;)

<script type="text/javascript" src="somewhere.js">;</script>
Goldsworthy answered 24/11, 2010 at 15:22 Comment(0)
D
2

You can't tell your browser to handle invalid HTML as HTML -- you're lucky it understands malformed HTML at all. :)

Definitely do this in your stylesheet:

<xsl:output method="html"/>

But, if your source document has namespaces, this won't do the trick. XSLT processors seem to silently change the output method back to XML if namespace nodes are present in the output.

You need to replace all instances of <xsl:copy-of> and <xsl:copy> with creations of elements with just the local name, e.g.

<xsl:template match="*">
   <xsl:element name="{local-name()}">
     <xsl:apply-templates/>
   </xsl:element>
</xsl:template>

See

etc.

Dissidence answered 20/5, 2009 at 13:8 Comment(3)
I have only the default namespace : xmlns:xsl="w3.org/1999/XSL/Transform As you said, I think he switch back to xml.Rosenarosenbaum
I meant the namespace in your source data document, not the XSLT document.Dissidence
Ah ok, sorry. I don't use namespace in the soruce document. I think it's the fact I use an XmlTextWriter that force the "xml behavior". Anyways, thank you very much for your help and for your time.Rosenarosenbaum
C
2

I used to put an <xsl:text> element inside, like:

<script type="text/javascript" src="/scripts/jquery.js"><xsl:text> </xsl:text></script>
Cupreous answered 18/7, 2011 at 22:5 Comment(0)
C
2

The easy way I found was creating a new XmlTextWriter class to override the method WriteEndElement, forcing the non-closing tag and pass on the serialization process as parameter.

public class MyXmlTextWriter : XmlTextWriter
{
    public MyXmlTextWriter(Stream stream) : base(stream, Encoding.UTF8)
    { }
    public MyXmlTextWriter(TextWriter stream) : base(stream)
    { }

    public override void WriteEndElement()
    {
        base.WriteFullEndElement();
    }
}
Consequence answered 20/2, 2018 at 23:43 Comment(0)
R
1

There are a few things you need to be careful:

  1. In your xsl use < xsl:output method='html'>
  2. set OutputSettings in your output XmlWriter
  3. in the Html inside your xsl, don't set attributes in html tag like this < html xmlns="http://www.w3.org/1999/xhtml"> but use < html> instead.

This is a piece of working code:

string xmlStr = "<?xml version='1.0' encoding='UTF-8'?><Data></Data>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlStr);
string xslContents = @"
<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'  
xmlns:msxsl='urn:schemas-microsoft-com:xslt' exclude-result-prefixes='msxsl'>
<xsl:output method='html' version='4.0' omit-xml-declaration='yes' indent='yes'/>
<xsl:template match='Data'>
<html>
<body>
    <div></div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>";
XslCompiledTransform xsl = new XslCompiledTransform();
xsl.Load(XmlReader.Create(new StringReader(xslContents)));
StringWriter result = new StringWriter();
using (XmlWriter writer = XmlWriter.Create(result, xsl.OutputSettings))
{
    xsl.Transform(doc, null, writer);
}
System.Diagnostics.Debug.Write( result.ToString());
Renewal answered 6/4, 2014 at 7:10 Comment(1)
Using the output method on the stylesheet was the only solution that worked for me. I need to transform an XML via a Stylesheet to HTML, and was unable to use the XmlWriter object, only HtmlTextWriter which does not have the same settings. Long story short this worked for me although I don't love it that much as if I could make it programmatically.Contrivance
C
0

Just experienced the same issue with PHP 5's XSL, with output/@method=html. Seems that assigning an empty value attribute will cause elements to be output as invalid non-self-closing, non-closed tags:

<input type="text" name="foo" value="{my-empty-value}" />

results in:

<input type="text" name="foo" value="">

One possible solution is to conditionally add the attribute:

<xsl:if test="string-length(my-empty-value) > 0">
    <xsl:attribute name="value">
        <xsl:value-of select="my-empty-value" />
    </xsl:attribute>
</xsl:if>

resulting in:

<input type="text" name="foo" />
Contend answered 24/9, 2012 at 14:57 Comment(1)
It seems that you're answering a different question: how to get a self-closing tag instead of a non-closed tag. The OP asks how to get a closed, non-self-closing tag instead of a self-closing tag. Am I right?Markson
H
0

I use the following whenever I wish to prevent an element from self-closing:

<xsl:value-of select="''" />

This fools the rendering engine into believe there is content inside the element, and therefore prevents self-closure.

It's a bit of an ugly fix so I recommend containing it in a descriptive template and calling that each time instead:

<xsl:template name="PreventSelfClosure">
   <xsl:value-of select="''" />
</xsl:template>


<div class="test">
   <xsl:call-template name="PreventSelfClosure"/>
</div>

This will then render the following:

<div class="test"></div>

http://curtistimson.co.uk/post/xslt/how-to-prevent-self-closing-elements-in-xslt/

Hernadez answered 2/7, 2014 at 15:5 Comment(0)
P
0

Don't try this at home:

<xsl:when test="self::* and not(text())">
    <xsl:value-of select="concat('&lt;', name(), '&gt;', '&lt;/', name(), '&gt;')" disable-output-escaping="yes"/>
</xsl:when>
Presidency answered 2/3, 2020 at 23:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.