Подтвердить что ты не робот

Могу ли я предоставить пользовательскую сериализацию для XmlSerializer без реализации IXmlSerializable?

Мы используем XmlSerializer, и я хочу предоставить пользовательскую сериализацию для определенных классов. Однако у меня не всегда есть возможность изменять исходный код рассматриваемого класса, иначе я мог бы просто реализовать его IXmlSerializable. Есть ли способ сделать это?

4b9b3361

Ответ 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;
        }
    }
}