Using the XmlSerializer as an XmlObjectSerializer with WCF

Andrew Arnott

The Windows Communication Foundation introduced the new DataContractSerializer to replace the XmlSerializer in many scenarios.  The DataContractSerializer is faster than the XmlSerializer, but has certain limitations.  For example it does not support serializing an object’s members as XML attributes.  When you write a WCF client that calls a service, WCF will generate a proxy class that defaults to using the DataContractSerializer and falls back to the XmlSerializer if the service uses features that the DataContractSerializer does not support.  This fallback mechanism is automatic if you’re using WCF’s service model.

But if you’re working at WCF’s messaging layer, you must explicitly choose which serializer to use.  You can use any serializer that derives from System.Runtime.Serialization.XmlObjectSerializerSystem.Runtime.Serialization.DataContractSerializer already derives from this class — but System.Xml.Serialization.XmlSerializer does not.  So you have some work to do before you can use the XmlSerializer as your serializer of choice at the messaging layer of WCF.

In order to use the XmlSerializer to send messages with WCF, we have to define a class that derives from XmlObjectSerializer and uses the XmlSerializer internally.  In essence we will create a wrapper class for the XmlSerializer that makes it look and work like an XmlObjectSerializer.  XmlObjectSerializer is an abstract class with only a few methods we need to implement.

Below is a sample of a class that will do just that.

<span class="kwrd">namespace</span> Microsoft.ServiceModel.Samples
{
    <span class="kwrd">using</span> System;
    <span class="kwrd">using</span> System.Diagnostics;
    <span class="kwrd">using</span> System.IO;
    <span class="kwrd">using</span> System.Text;
    <span class="kwrd">using</span> System.Xml;
    <span class="kwrd">using</span> System.Xml.Serialization;
    <span class="kwrd">using</span> System.Runtime.Serialization;

    <span class="kwrd">public</span> <span class="kwrd">class</span> CFMessagingSerializer : XmlObjectSerializer
    {
        <span class="kwrd">readonly</span> Type objectType;
        XmlSerializer serializer;

        <span class="kwrd">public</span> CFMessagingSerializer(Type objectType)
            : <span class="kwrd">this</span>(objectType, <span class="kwrd">null</span>, <span class="kwrd">null</span>)
        {
        }
        
        <span class="kwrd">public</span> CFMessagingSerializer(Type objectType, <span class="kwrd">string</span> wrapperName, <span class="kwrd">string</span> wrapperNamespace)
        {
            <span class="kwrd">if</span> (objectType == <span class="kwrd">null</span>)
                <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException(<span class="str">"objectType"</span>);
            <span class="kwrd">if</span> ((wrapperName == <span class="kwrd">null</span>) != (wrapperNamespace == <span class="kwrd">null</span>))
                <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentException(<span class="str">"wrapperName and wrapperNamespace must be either both null or both non-null."</span>);
            <span class="kwrd">if</span> (wrapperName == <span class="kwrd">string</span>.Empty)
                <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentException(<span class="str">"Cannot be the empty string."</span>, <span class="str">"wrapperName"</span>);
            
            <span class="kwrd">this</span>.objectType = objectType;
            <span class="kwrd">if</span> (wrapperName != <span class="kwrd">null</span>)
            {
                XmlRootAttribute root = <span class="kwrd">new</span> XmlRootAttribute(wrapperName);
                root.Namespace = wrapperNamespace;
                <span class="kwrd">this</span>.serializer = <span class="kwrd">new</span> XmlSerializer(objectType, root);
            }
            <span class="kwrd">else</span>
                <span class="kwrd">this</span>.serializer = <span class="kwrd">new</span> XmlSerializer(objectType);
        }

        <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">bool</span> IsStartObject(XmlDictionaryReader reader)
        {
            <span class="kwrd">throw</span> <span class="kwrd">new</span> NotImplementedException();
        }

        <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">object</span> ReadObject(XmlDictionaryReader reader, <span class="kwrd">bool</span> verifyObjectName)
        {
            Debug.Assert(serializer != <span class="kwrd">null</span>);
            <span class="kwrd">if</span> (reader == <span class="kwrd">null</span>) <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException(<span class="str">"reader"</span>);
            <span class="kwrd">if</span> (!verifyObjectName)
                <span class="kwrd">throw</span> <span class="kwrd">new</span> NotSupportedException();

            <span class="kwrd">return</span> serializer.Deserialize(reader);
        }
        
        <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> WriteStartObject(XmlDictionaryWriter writer, <span class="kwrd">object</span> graph)
        {
            <span class="kwrd">throw</span> <span class="kwrd">new</span> NotImplementedException();
        }

        <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> WriteObjectContent(XmlDictionaryWriter writer, <span class="kwrd">object</span> graph)
        {
            <span class="kwrd">if</span> (writer == <span class="kwrd">null</span>) <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException(<span class="str">"writer"</span>);
            <span class="kwrd">if</span> (writer.WriteState != WriteState.Element)
                <span class="kwrd">throw</span> <span class="kwrd">new</span> SerializationException(<span class="kwrd">string</span>.Format(<span class="str">"WriteState '{0}' not valid. Caller must write start element before serializing in contentOnly mode."</span>,
                    writer.WriteState));
            <span class="kwrd">using</span> (MemoryStream memoryStream = <span class="kwrd">new</span> MemoryStream())
            {
                <span class="kwrd">using</span> (XmlDictionaryWriter bufferWriter = XmlDictionaryWriter.CreateTextWriter(memoryStream, Encoding.UTF8))
                {
                    serializer.Serialize(bufferWriter, graph);
                    bufferWriter.Flush();
                    memoryStream.Position = 0;
                    <span class="kwrd">using</span> (XmlReader reader = <span class="kwrd">new</span> XmlTextReader(memoryStream))
                    {
                        reader.MoveToContent();
                        writer.WriteAttributes(reader, <span class="kwrd">false</span>);
                        <span class="kwrd">if</span> (reader.Read()) <span class="rem">// move off start node (we want to skip it)</span>
                        {
                            <span class="kwrd">while</span> (reader.NodeType != XmlNodeType.EndElement) <span class="rem">// also skip end node.</span>
                                writer.WriteNode(reader, <span class="kwrd">false</span>); <span class="rem">// this will take us to the start of the next child node, or the end node.</span>
                            reader.ReadEndElement(); <span class="rem">// not necessary, but clean</span>
                        }
                    }
                }
            }
        }

        <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> WriteEndObject(XmlDictionaryWriter writer)
        {
            <span class="kwrd">throw</span> <span class="kwrd">new</span> NotImplementedException();
        }
        
        <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> WriteObject(XmlDictionaryWriter writer, <span class="kwrd">object</span> graph)
        {
            Debug.Assert(serializer != <span class="kwrd">null</span>);
            <span class="kwrd">if</span> (writer == <span class="kwrd">null</span>) <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException(<span class="str">"writer"</span>);
            serializer.Serialize(writer, graph);
        }
    }
}

0 comments

Discussion is closed.

Feedback usabilla icon