DataContractSerializer.ReadObject is easily confused.

With WCF services you need to declare contracts and generate contract classes that encapsulate those contracts.  Most of the time you can simply let the framework deal with whatever it needs to do to deal with these objects.  Sometimes, you need to actually see without running a service what XML would result from a contract object or serialize a contract object from XML text.

In .NET 3.5 there exists the System.Runtime.Serialization.DataContractSerializer class that perform serialization of data contracts.

Based on its documentation it seems fairly simple to create data contract object to/from XML methods.  For example:

    public static T XmlToContractObject<T>(string xml) where T : class, IExtensibleDataObject

    {

        MemoryStream memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(xml));

        using (

        XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(memoryStream, Encoding.Unicode,

                   new XmlDictionaryReaderQuotas(), null))

        {

            DataContractSerializer dataContractSerializer = new DataContractSerializer(typeof(T));

            return dataContractSerializer.ReadObject(reader) as T;

        }

    }

 

    public static string ContractObjectToXml<T>(T obj) where T : IExtensibleDataObject

    {

        DataContractSerializer dataContractSerializer = new DataContractSerializer(obj.GetType());

 

        String text;

        using (MemoryStream memoryStream = new MemoryStream())

        {

            dataContractSerializer.WriteObject(memoryStream, obj);

 

            text = Encoding.UTF8.GetString(memoryStream.GetBuffer());

        }

        return text;

    }

But, when you try to perform a round-trip from a contract object to XML and back you’ll notice that you get SerializationException with the cryptic message of "There was an error deserializing the object of type <sometype>.  The data at the root level is invalid", despite that the XML seems view when examined.

When Encoding.UTF8.GetString is called, it literally translates the entire array into a String object.  This means that it reads any and all 0 bytes in the array and dumps out null characters (”) into the string.  Why do I mention this?  Well, MemoryStream uses a self-expanding buffer to write to.  When MemoryStream runs out of space in its buffer, it doubles the size of the buffer, zeroes it out, then copies the original buffer to the start of the new buffer.  When MemoryStream.GetBuffer is called it simply gets a reference to this buffer, regardless of how many bytes have been written to the stream.  So, most of the time you get a buffer with zeroes padded to the end of it.

If you look closely at the resulting XML, there are a bunch of ” characters at the end of the string.

As it turns out, DataContractSerializer.ReadObject (or something it calls) doesn’t like all the extra null characters and this is the cause of the SerializationException.

Irritating as this may be, the fix is fairly straightforward.  Simply, create a new buffer equal to the length of the stream and copy the data from the MemoryStream buffer to the new one–trimming all the extra zeroes.  For example:

    public static string ContractObjectToXml<T>(T obj) where T : IExtensibleDataObject

    {

        DataContractSerializer dataContractSerializer = new DataContractSerializer(obj.GetType());

 

        String text;

        using (MemoryStream memoryStream = new MemoryStream())

        {

            dataContractSerializer.WriteObject(memoryStream, obj);

            byte[] data = new byte[memoryStream.Length];

            Array.Copy(memoryStream.GetBuffer(), data, data.Length);

 

            text = Encoding.UTF8.GetString(data);

        }

        return text;

    }

Digg ThisDotNetKick This

4 thoughts on “DataContractSerializer.ReadObject is easily confused.”

  1. Actually your problem is that you’re calling MemoryStream.GetBuffer() which gets the raw underlying buffer used by the MemoryStream complete with padding. You should be using MemoryStream.ToArray() instead which returns a byte[] that is truncated to the length of the stream. Basically it does what you’re already doing, except that it’s built-in.

  2. Setting the position to 0 in the memorystream after writing the data and then using a StreamReader to read the data from the memorystream works great as well. I find it more readable… (and it defaults to unicode)

  3. This is what I’m using and it works!

    public static string SerializeDataContract(TModel dataContract)
    {
    var serializer = new DataContractSerializer(typeof(TModel));

    using (var stream = new MemoryStream())
    {
    serializer.WriteObject(stream, dataContract);
    stream.Position = 0L;
    return Encoding.UTF8.GetString(stream.ToArray());
    }
    }

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>