Мы используем XmlSerializer
, и я хочу предоставить пользовательскую сериализацию для определенных классов. Однако у меня не всегда есть возможность изменять исходный код рассматриваемого класса, иначе я мог бы просто реализовать его IXmlSerializable
. Есть ли способ сделать это?
Могу ли я предоставить пользовательскую сериализацию для XmlSerializer без реализации IXmlSerializable?
Ответ 1
Вот простой пример помощника прокси-десериализации:
Учитывая тип, который мы не можем напрямую контролировать сериализацию на уровне класса:
public sealed class Class //contrived example
{
public string Property {get;set;}
}
И xml нам нужно десериализовать с помощью:
<Class>
<Property>Value</Property>
</Class>
Вы можете создать прокси-тип, чтобы вручную обрабатывать процесс десериализации целевого типа следующим образом:
[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;
}
}
Использование:
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);
}
Теперь введите некоторые обобщения и метод расширения, и мы можем немного очистить сайт вызова для окончательной версии (EXCEPT EXCEPTION HANDLING):
Использование:
void Main()
{
var xml = @"<Class><Property>Value</Property></Class>";
var value = xml.DeserializeWithProxy<ClassSerializerProxy,Class>();
value.Dump();
}
Тип вашего экземпляра:
public sealed class Class
{
public string Property {get;set;}
}
Интерфейс, который должны использовать прокси-типы
public interface ISerializerProxy<TInstanceType> where TInstanceType : class
{
TInstanceType Value { get; }
}
Пример прокси теперь реализует новый интерфейс
[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;
}
}
Метод десериализации теперь является методом расширения на string
и может использоваться с любым типом прокси.
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;
}
}
}