Search code examples
c#.netxmlwcf

How can I set up an XmlWriter with the same settings as XmlDictionaryWriter?


I'm currently using

DataContractSerializer dcs = new DataContractSerializer(typeof(T));
XmlDictionaryWriter xdw = XmlDictionaryWriter.CreateTextWriter(filestream, Encoding.UTF8);
dcs.WriteObject(xdw, obj);

In order to write out XML, I've heard good things about the WCF DataContractSerializer in terms of it's performance, and ability to provide forwards compatibility.

However, it's impossible to pass in settings to XmlDictionaryWriter.

I don't 100% understand the differences between XmlDictionaryWriter and a normal XmlWriter with custom settings, and it's impossible to tweak the settings of XmlDictionaryWriter as far as I'm aware.

So what are the differences between XmlDictionaryWriter and XmlWriter (yes one is a super class, but I'm talking concretely, vs var XmlWriter = XmlWriter.Create(filestream, settings);)

And what settings can I use in order to imitate XmlDictionaryWriter as close as possible, except for having indentation set to true?

I've currently got

var settings = new XmlWriterSettings
            {
                Indent = true,
                Encoding = Encoding.UTF8,
                IndentChars = "    "
            };

As my settings for the XmlWriter, whereas XmlDictionaryWriter appears to have null Settings. (XmlDictionaryWriter.Settings is both null and readonly, so that's a bust.)

My end goal is to have formatted XML, so maybe if the changes aren't too severe I can just use a hand created XmlWriter anyway.

Comparing the two using NUnit results in

XmlDictionaryWriter:

"<Party xmlns=\"http://schemas.datacontract.org/2004/07/HeliSTATS.Test\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><Guests><Larry><Age>12</Age><Friend><Name>John</Name></Friend></Larry><Larry><Age>15</Age><Friend><Name>Mason</Name></Friend></Larry></Guests></Party>"

XmlWriter:

"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Party xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://schemas.datacontract.org/2004/07/HeliSTATS.Test\">\r\n    <Guests>\r\n        <Larry>\r\n            <Age>12</Age>\r\n            <Friend>\r\n                <Name>John</Name>\r\n            </Friend>\r\n        </Larry>\r\n        <Larry>\r\n            <Age>15</Age>\r\n            <Friend>\r\n                <Name>Mason</Name>\r\n            </Friend>\r\n        </Larry>\r\n    </Guests>\r\n</Party>"

Solution

  • You're actually asking a few separate questions:

    1. What is the differences between XmlDictionaryWriter and XmlWriter?

      This question is addressed in XMLWriter vs XMLDictionaryWriter with answers by Bronumski and Mike McCaughan. The conclusion is that XmlDictionaryWriter is used

      • For performance (though we don't know for sure), and
      • As an abstract base class for XML, Binary and MTOM serialization of data contracts.


      In addition, some functionality of DataContractSerializer only works with XmlDictionaryWriter. E.g. if you need to serialize an object using a custom DataContractResolver you will need to use DataContractSerializer.WriteObject(XmlDictionaryWriter, Object, DataContractResolver) because there is no overload of WriteObject() that takes both an XmlWriter and a DataContractResolver.

    2. How can I use XmlDictionaryWriter and also control XmlWriterSettings?

      If you are sure you need to use XmlDictionaryWriter (for performance reasons, or because you need to use a DataContractResolver) then you can construct one that wraps a pre-existing XmlWriter by using XmlDictionaryWriter.CreateDictionaryWriter(XmlWriter).

    3. What settings can I use in order to imitate XmlDictionaryWriter with XmlWriter as closely as possible while still indenting?

      You don't. You use the one you need, and if you need the functionality of both, create a wrapped XmlWriter as shown above.

    Putting all of the above together, the following extension methods can be used to serialize with both XmlDictionaryWriter and optional XmlWriterSettings:

    public static partial class DataContractSerializerExtensions
    {
        public static string ToContractXml<T>(this T obj, DataContractSerializer serializer = null, XmlWriterSettings settings = null, DataContractResolver resolver = null)
        {
            serializer = serializer ?? new DataContractSerializer(obj == null ? typeof(T) : obj.GetType());
            using (var textWriter = new StringWriterWithEncoding((settings == null ? null : settings.Encoding) ?? Encoding.UTF8))
            {
                using (var xmlWriter = XmlWriter.Create(textWriter, settings))
                {
                    serializer.WriteObject(xmlWriter, obj, resolver);
                }
                return textWriter.ToString();
            }
        }
    
        public static void WriteObject(this DataContractSerializer serializer, Stream stream, object obj, XmlWriterSettings settings, DataContractResolver resolver = null)
        {
            if (serializer == null || stream == null)
                throw new ArgumentNullException();
            // If settings are specified create a wrapped dictionary writer, else create a text writer directly.
            if (settings == null)
            {
                // Let caller close the stream
                using (var xmlWriter = XmlDictionaryWriter.CreateTextWriter(stream, Encoding.UTF8, false))
                {
                    serializer.WriteObject(xmlWriter, obj, resolver);
                }
            }
            else
            {
                using (var xmlWriter = XmlWriter.Create(stream, settings))
                {
                    serializer.WriteObject(xmlWriter, obj, resolver);
                }
            }
        }
    
        static void WriteObject(this DataContractSerializer serializer, XmlWriter xmlWriter, object obj, DataContractResolver resolver)
        {
            if (serializer == null || xmlWriter == null)
                throw new ArgumentNullException();
            using (var xmlDictionaryWriter = XmlDictionaryWriter.CreateDictionaryWriter(xmlWriter))
            {
                serializer.WriteObject(xmlDictionaryWriter, obj, resolver);
            }
        }
    }
    

    Then, profile WriteObject() with and without XmlWriterSettings to determine the performance impact of using a wrapped XmlWriter.