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?
            Asked
            
        
        
            Active
            
        
            Viewed 2.3k times
        
    20
            
            
        
        Rob
        
- 4,327
 - 6
 - 29
 - 55
 
- 
                    I know you can adorn the classes with various XmlAttributes (like `XmlElement`, `XmlArray`, etc), but you're asking specifically about how to control the XML output of non-modifiable classes? – Chris Sinclair Jun 21 '13 at 13:50
 - 
                    1I use "proxy" classes that impliment IXmlSerializable with the given root tag name and use the ReadXml function to construct the real underlying value, which is then exposed in a public property. – asawyer Jun 21 '13 at 13:50
 - 
                    Well there's this [constructor overload](http://msdn.microsoft.com/en-us/library/65k4wece.aspx) which lets you override the attributes used on the types, but I don't know if it lets you alter the behaviour/result of `IXmlSerializable.ReadXml`. EDIT: Can you separate the serialization concerns between your business objects and the 3rd party objects? – Chris Sinclair Jun 21 '13 at 13:53
 - 
                    @Chris. Yes - I want to control the Xml output of non-modifiable classes. Specifically, the non-modifiable classes don't permit the default serialization, in that they have some properties with private setters, etc. – Rob Jun 21 '13 at 15:12
 - 
                    @asawyer. That could work. If I serialize a top level object of type X that has property of type "problem" class Y, how would I go about using a proxy for Y? – Rob Jun 21 '13 at 15:18
 - 
                    @Rob I'll write up an example please give me a few minutes. – asawyer Jun 21 '13 at 17:01
 - 
                    @Rob I dont know if its the solution you need, but I wrote up a working example for you. – asawyer Jun 21 '13 at 17:52
 
1 Answers
16
            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;
        }
    }
}
        asawyer
        
- 17,642
 - 8
 - 59
 - 87
 
- 
                    13Is there a way to use this approach if I have a large object tree, and want to use the default serialization for the tree as a whole, but custom serialization for certain leaves? If I have access to the source, I can make those leaves implement IXmlSerializable, and put custom serialization there. But if I can't modify the leaves, do I need to write serialization code for the entire tree? Or can I just target the small subset of problem classes somehow? – Rob Jun 25 '13 at 08:16
 - 
                    1@asawyer This is a great answer! I chose to also implement the WriteXml functionality since my real class is not XML serializable in itself. It consists of a bunch of read-only accessors which can only be set through the constructor. – Nathan Phetteplace Aug 30 '13 at 14:02
 - 
                    
 - 
                    1@asawyer I'd be interested in knowing if what Rob asked is possible with this code. – The Muffin Man Dec 22 '14 at 19:04
 - 
                    @TheMuffinMan I don't know honestly. It sounds plausible enough. If I had that sort of need I'd give it a shot. Perhaps asking a new question may help? – asawyer Dec 22 '14 at 19:22
 - 
                    1@asawyer The question is here: https://stackoverflow.com/questions/44290214/contract-first-wcf-soap-development-controlling-over-primitive-types-xml-serial – Anton Krouglov May 31 '17 at 16:40