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?
Can I provide custom serialization for XmlSerializer without implementing IXmlSerializable?
Asked Answered
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;
}
}
}
Is 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? –
Lapillus
@Subroutine 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. –
Cirri
@natephette I'm so happy I was able to help! –
Subroutine
@Subroutine I'd be interested in knowing if what Rob asked is possible with this code. –
Kansu
@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? –
Subroutine
@Subroutine The question is here: #44290714 –
Carruthers
© 2022 - 2024 — McMap. All rights reserved.
XmlElement
,XmlArray
, etc), but you're asking specifically about how to control the XML output of non-modifiable classes? – CourtneyIXmlSerializable.ReadXml
. EDIT: Can you separate the serialization concerns between your business objects and the 3rd party objects? – Courtney