How to use XmlWriterSettings() when using override void WriteEndElement()? How to use XmlWriterSettings() when using override void WriteEndElement()? xml xml

How to use XmlWriterSettings() when using override void WriteEndElement()?


How about this.

Grab the awesome XmlWrappingWriter class from http://www.tkachenko.com/blog/archives/000585.html (I have omitted the code for the sake of brevity).

With that, we can create a sub-class as follows (very similar to your original one):

public class XmlTextWriterFull2 : XmlWrappingWriter{    public XmlTextWriterFull2(XmlWriter baseWriter)        : base(baseWriter)    {    }    public override void WriteEndElement()    {        base.WriteFullEndElement();    }}

It can then be invoked like this (again very similar):

var x_settings = new XmlWriterSettings();x_settings.NewLineChars = Environment.NewLine;x_settings.NewLineOnAttributes = true;x_settings.NewLineHandling = NewLineHandling.None;x_settings.CloseOutput = true;x_settings.Indent = true;x_settings.NewLineOnAttributes = true;using (XmlWriter writer = XmlWriter.Create(outputFilename, x_settings)){    using (XmlTextWriterFull2 xmlTextWriterFull = new XmlTextWriterFull2(writer))    {        var x_serial = new XmlSerializer(typeof(YOUR_OBJECT_TYPE));        x_serial.Serialize(xmlTextWriterFull, YOUR_OBJECT_INSTANCE);    }}

In my case, an element that had previously been rendered as

<Foo></Foo>

became

<Foo></Foo>

As you alluded to in your question, this is actually quite a tricky problem due to everything being sealed/internal etc., making overrides rather difficult. I think my biggest problem was trying to get an XmlWriter to accept XmlWriterSettings: beyond this approach, I could find no way of getting the original XmlTextWriterFull to respect the given XmlWriterSettings.

MSDN states that this method:

XmlWriter.Create(XmlWriter, XmlWriterSettings)

Can be used to apply the XmlWriterSettings to the XmlWriter. I couldn't get this to work like I wanted (the indentation never worked, for example), and upon decompiling the code, it does not appear that all the settings are used with this particular method, hence why my invocation code just passes in the outputFile (a stream of some sort would work just as well).


The workaround solution you gave in your question adds extra line breaks (when indenting is enabled) because we're telling the writer to treat this element as if it had children.

Here is how I modified your workaround to manipulate the indenting dynamically so as to avoid those extra line breaks.

public class XmlTextWriterFull : XmlTextWriter{    public XmlTextWriterFull(TextWriter sink)        : base(sink)    {        Formatting = Formatting.Indented;    }    private bool inElement = false;    public override void WriteStartElement(string prefix, string localName, string ns)    {        base.WriteStartElement(prefix, localName, ns);        // Remember that we're in the process of defining an element.        // As soon as a child element is closed, this flag won't be true anymore and we'll know to avoid messing with the indenting.        this.inElement = true;    }    public override void WriteEndElement()    {        if (!this.inElement)        {            // The element being closed has child elements, so we should just let the writer use it's default behavior.            base.WriteEndElement();        }        else        {            // It looks like the element doesn't have children, and we want to avoid emitting a self-closing tag.            // First, let's temporarily disable any indenting, then force the full closing element tag.            var prevFormat = this.Formatting;            this.Formatting = Formatting.None;            base.WriteFullEndElement();            this.Formatting = prevFormat;            this.inElement = false;        }    }}


Following code snippet force printing of closing tag on the same line (sorry for vb version, it should be easy to rewrite the same using C#):

Imports System.XmlImports System.IOPublic Class CustomXmlTextWriter    Inherits XmlTextWriter    Public Sub New(ByRef baseWriter As TextWriter)        MyBase.New(baseWriter)        Formatting = Xml.Formatting.Indented    End Sub    Public Overrides Sub WriteEndElement()        If Not (Me.WriteState = Xml.WriteState.Element) Then            MyBase.WriteEndElement()        Else            Formatting = Xml.Formatting.None            MyBase.WriteFullEndElement()            Formatting = Xml.Formatting.Indented        End If    End SubEnd Class