0

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>"
dbc
  • 104,963
  • 20
  • 228
  • 340
Ryan Leach
  • 4,262
  • 5
  • 34
  • 71
  • Have you considered ditching them both and using the more modern XDocument, XElement, and XAttribute classes from Linq? Also do you have requirements for WCF instead of using WebApi2 and Rest? WebApi2 respects content type headers, so will give XML to anyone who asks for it, or json to anyone who asks for it. – Ryan Mann Jan 25 '18 at 07:39
  • I wasn't aware. I'll look into it. I was drawn to DataContractSerializer due to explicit annotations and working without being public properties. – Ryan Leach Jan 25 '18 at 07:41
  • WebApi2 and Rest requires people to write there own interfaces to the results, but WebApi2 is pretty slick in building them, you can annotate your entities, easily do middle ware, and easily test them in PostMan etc. This is how I prefer to do things. I make an API, then I make a class library that is the "Client" that calls that Api, or I have a JS layer in Angular or Vue that calls the API via Ajax requests. Almost every modern api made today is REST. – Ryan Mann Jan 25 '18 at 07:44
  • Thanks for the tips, I'm using this for some super simple configs at the moment, which is why I'm caring about indentation, about limiting visibility modifiers. I'm not sure how suitable stuff designed for REST will be, but DCS mostly seems fine. – Ryan Leach Jan 25 '18 at 07:46
  • I've had a quick read over, I can see how it could be useful for transformation, or bulk xml processing. But it seems almost too flexible to me. I prefer the XML as Code/Classes approach since it feel's more like a schema, and makes it easier to validate IMO. But I can see where LINQ would be handy for some extremely dynamic XML, or even JSON. – Ryan Leach Jan 25 '18 at 07:53
  • 1
    For large xml files I would stick with XmlWriter so you can save small pieces of the file at one time. I combine the XmlWriter and XDocument by using the XmlWriter writer.WriteRaw(string). Then Take may XElement object and convert to a string object.ToString(). In a few case at top of Xml I have to use StartElement and EndElement. – jdweng Jan 25 '18 at 08:02

1 Answers1

1

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.

dbc
  • 104,963
  • 20
  • 228
  • 340