How to force System.Xml.Xsl.XslCompiledTransform class to not use self closing tags for empty nodes

In .NET 2.0 framework when you use System.Xml.Xsl.XslCompiledTransform class for transforming a document then we always get self closing tags for all the elements which have nothing inside their body. There is no direct way to ask it to use separate closing tags

e.g.

<BODY>

<TABLE></TABLE>

<BR></BR>

</BODY>

transforms as

<BODY>

<TABLE/>

<BR/>

</BODY>

 

This might cause problems when we are transforming a document for HTML processing as HTML doesn't identify self closing tags properly. So although <Table/> is a valid XML closed tag but HTML identifies it as starting of a table only which causes its output to be different than what we want.

 

The Xslcompiledtransform class uses self closing tags for all the elements irrespective of whether they are empty or not.

This didn't used to happen with System.Xml.Xsl.XslTransform class in .NET 1.1 framework. It used separate closing tags for empty elements.

One way to resolve this issue is to override the default WriteEndElement behaviour of XmlTextWriter class using inheritence. In the overriden method we will write non self closing nodes even if the node is empty. In the Transform object we will use this derived writer object instead of the XmlTextWriter object itself.

Here are the steps.

· Create a new class XmlHtmlWriter inherited from XmlTextWriter.

· Override two functions WriteStartElement and WriteEndElement.

· Create a list fullyClosedElements for storing names of all the tags which you want not to be self closed

· In WriteStartElement function record the tag which is being written and in WriteEndElement check whether we need to close this element using separate closing tag or not by checking whether it exists in fullyClosedElements list or not.

· To use separate tag for closing call WriteFullEndElement function else call base.WriteEndElement .

· Then use an instance of this XmlHtmlWriter class in your application for output of xsl transform.

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Xml;

 

namespace XSLSelfClosing

{

    public class XmlHtmlWriter : XmlTextWriter

    {

        public XmlHtmlWriter(System.IO.Stream stream, Encoding en)

            : base(stream, en)

        {

            //Put all the elemnts for which you want self closing tags in this list.

            //Rest of the tags would be explicitely closed

            fullyClosedElements.AddRange(new string[] { "br", "hr" });

        }

        string openingElement = "";

        List<string> fullyClosedElements = new List<string>();

        public override void WriteEndElement()

        {

            if (fullyClosedElements.IndexOf(openingElement) < 0)

                WriteFullEndElement();

            else

                base.WriteEndElement();

        }

 

        public override void WriteStartElement(string prefix, string localName, string ns)

        {

            base.WriteStartElement(prefix, localName, ns);

            openingElement = localName;

        }

    }

}

 

Author : Naresh Joshi (MSFT) , SQL Developer Technical Lead, Microsoft