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?
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.
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.
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.
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.
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;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With