Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I provide custom serialization for XmlSerializer without implementing IXmlSerializable?

We're using XmlSerializer, and I want to provide custom serialization for certain classes. However, I don't always have the ability to modify the source code of the class in question, otherwise I could just make it implement IXmlSerializable. Is there any way to do this?

like image 624
Rob Avatar asked Jun 21 '13 13:06

Rob


People also ask

Can I make XmlSerializer ignore the namespace on Deserialization?

Yes, you can tell the XmlSerializer to ignore namespaces during de-serialization. Note this is the kind of thing I meant. You are not telling the XmlSerializer to ignore namespaces - you are giving it XML that has no namespaces.

What is System XML serialization XmlElementAttribute?

The XmlElementAttribute belongs to a family of attributes that controls how the XmlSerializer serializes or deserializes an object. For a complete list of similar attributes, see Attributes That Control XML Serialization.

How does the XmlSerializer work C#?

The XmlSerializer creates C# (. cs) files and compiles them into . dll files in the directory named by the TEMP environment variable; serialization occurs with those DLLs. These serialization assemblies can be generated in advance and signed by using the SGen.exe tool.

Is XmlSerializer thread safe?

Since XmlSerializer is one of the few thread safe classes in the framework you really only need a single instance of each serializer even in a multithreaded application.


1 Answers

Here's a simple example of the proxy deserialize helper:

Given a type that we cannot directly control serialization of at the class level:

public sealed class Class //contrived example
{
    public string Property {get;set;}
}

And the xml we need to deserialize with:

<Class>
  <Property>Value</Property>
</Class>

You could create a proxy type to manually process the deserialization process of the target type like so:

[XmlRoot("Class")] // <-- Very important
public sealed class ClassSerializerProxy : IXmlSerializable
{
    public Class ClassValue {get;set;}

    public System.Xml.Schema.XmlSchema GetSchema(){return null;}
    public void WriteXml(System.Xml.XmlWriter writer){}

    public void ReadXml(System.Xml.XmlReader reader)
    {
        var x = XElement.ReadFrom(reader) as XElement;
        this.ClassValue = new Class();
        //again this is a simple contrived example
        this.ClassValue.Property = x.XPathSelectElement("Property").Value;
    }
}

Usage is:

void Main()
{
    // get the xml value somehow
    var xdoc= XDocument.Parse(@"<Class><Property>Value</Property></Class>");

    // deserialize the xml into the proxy type
    var proxy = Deserialize<ClassSerializerProxy>(xdoc);

    // read the resulting value
    var value = proxy.ClassValue;
}

public object Deserialize(XDocument xmlDocument, Type DeserializeToType)
{
    XmlSerializer xmlSerializer = new XmlSerializer(DeserializeToType);
    using (XmlReader reader = xmlDocument.CreateReader())
        return xmlSerializer.Deserialize(reader);
}

Now throw in some generics and an extension method, and we can clean the call site up a bit for a final (EXCEPT EXCEPTION HANDLING) version:

Usage:

void Main()
{
    var xml = @"<Class><Property>Value</Property></Class>";

    var value = xml.DeserializeWithProxy<ClassSerializerProxy,Class>();

    value.Dump();
}

Your instance type:

public sealed class Class
{
    public string Property {get;set;}
}

An interface that proxy types must implement

public interface ISerializerProxy<TInstanceType> where TInstanceType : class
{
    TInstanceType Value { get; }
}

The example proxy now implements the new interface

[XmlRoot("Class")]
public sealed class ClassSerializerProxy : IXmlSerializable, ISerializerProxy<Class>
{
    public Class Value {get;set;}

    public System.Xml.Schema.XmlSchema GetSchema(){return null;}
    public void WriteXml(System.Xml.XmlWriter writer){}

    public void ReadXml(System.Xml.XmlReader reader)
    {
        var x = XElement.ReadFrom(reader) as XElement;
        this.Value = new Class();
        this.Value.Property = x.XPathSelectElement("Property").Value;
    }
}

The deserialization method is now an extension method on string and can be used with any proxy type.

public static class ExtensionMethods
{
    public static TInstanceType DeserializeWithProxy<TProxyType,TInstanceType>(this string xml) 
        where TProxyType : ISerializerProxy<TInstanceType> 
        where TInstanceType : class
    {
        using (XmlReader reader = XDocument.Parse(xml).CreateReader())
        {
            var xmlSerializer = new XmlSerializer(typeof(TProxyType));
            return (xmlSerializer.Deserialize(reader) as ISerializerProxy<TInstanceType>).Value;
        }
    }
}
like image 59
asawyer Avatar answered Sep 28 '22 05:09

asawyer