Переопределение десериализации XML для использования базовой десериализации и добавления функциональности - PullRequest
10 голосов
/ 02 апреля 2012

У меня есть класс, который нужно сериализовать и десериализовать.

Но каждый раз после десерилизации мне нужно вызывать метод синхронизации ссылок.

В любом случае, я могу реализовать десериализацию и использовать традиционную десериализацию, но добавить вызов к моему методу после регулярной десериализации?

Ответы [ 3 ]

8 голосов
/ 05 апреля 2012
using System.Xml.Serialization;

namespace Custom.Xml.Serialization
{
    public interface IXmlDeserializationCallback
    {
        void OnXmlDeserialization(object sender);
    }

    public class CustomXmlSerializer : XmlSerializer
    {
        protected override object Deserialize(XmlSerializationReader reader)
        {
            var result = base.Deserialize(reader);

            var deserializedCallback = result as IXmlDeserializationCallback;
            if (deserializedCallback != null)
            {
                deserializedCallback.OnXmlDeserialization(this);
            }

            return result;
        }
    }
}

наследует ваш класс от IXmlDeserializationCallback и реализует логику синхронизации в методе OnXmlDeserialization.

кредитов до Как узнать, когда вы были загружены с помощью XML-сериализации?

UPDATE:

ну, насколько я понимаю автор темы, он не хочет "вручную" вызывать какую-то логику после каждой десериализации XML. Поэтому вместо этого:

public class MyEntity
{
     public string SomeData { get; set; }

     public void FixReferences()
     {
          // call after deserialization
          // ...
     }
}

foreach (var xmlData in xmlArray)
{
    var xmlSer = new XmlSerializer(typeof(MyEntity));
    using (var memStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlData)))
    {
       var entity = (MyEntity)xmlSer.Deserialize(memStream);
       entity.FixReferences();

       // do something else with the entity
       // ...  
    }
}

он хочет сделать просто десериализацию, не беспокоясь о дополнительных вызовах. В этом случае предлагаемое решение является самым чистым / простым - вам нужно только унаследовать ваш класс сущности от интерфейса IXmlDeserializationCallback и заменить XmlSerializer на CustomXmlSerializer:

 public class MyEntity: IXmlDeserializationCallback
    {
         public string SomeData { get; set; }

         private void FixReferences()
         {
              // call after deserialization
              // ...
         }

         public void OnXmlDeserialization(object sender)
         {
             FixReferences();
         } 
    }

    foreach (var xmlData in xmlArray)
    {
        var xmlSer = new CustomXmlSerializer(typeof(MyEntity));
        using (var memStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlData)))
        {
           var entity = (MyEntity)xmlSer.Deserialize(memStream);
           // entity.FixReferences(); - will be called automatically

           // do something else with the entity
           // ...  
        }
    }
4 голосов
/ 08 апреля 2012

Ваши возможности использования XmlSerializer ограничены.

  • Десериализуйте весь ваш объектный граф, а затем примените все необходимые исправления.
  • Выполните некоторую обработку в установщиках свойств вашего объекта.
  • Реализуйте IXmlSerializable на ваших типах, чтобы иметь явный контроль сериализации / десериализации. Не простой вариант.

Если вы можете перейти на использование DataContractSerializer , который имеет свои преимущества (и недостатки), тогда вы можете использовать OnDeserializedAttribute . Например

[DataContract]
public class MyClass
{
    [DataMember]
    public string AMember{get;set;}

    [OnDeserialized]
    public void OnDeserialized(StreamingContext context)
    {
        // called after deserializing instance of MyClass
    }
}
1 голос
/ 10 апреля 2012

У меня есть хорошее решение 4 u

, напишите этот статический класс

public delegate void OnDeserializedEventArgs(object itemDeserialized, string xmlSource);
public delegate void OnDeserializedFailedEventArgs(string xmlSource);
public static class SerializationServices
{
    public static event OnDeserializedEventArgs OnDeserializedEvent;
    public static event OnDeserializedFailedEventArgs OnDeserializedFailedEvent;

    public static T Deserialize<T>(this string item) where T : class
    {
        XmlSerializer ser = new XmlSerializer(item.GetType());
        StringReader sr = new StringReader(item);
        var obj = ser.Deserialize(sr);

        if (obj is T)
        {
            if (OnDeserializedEvent != null)
                OnDeserializedEvent(obj, item);

            return obj as T;
        }

        if (OnDeserializedFailedEvent != null)
            OnDeserializedFailedEvent(item);

        //callback - invalid event
        return null;
    }
}

и затем используйте его с этим кодом

public class MyDesrializedClass
{
    //put some properties here...
}
class Program
{
    static void Main(string[] args)
    {
        SerializationServices.OnDeserializedEvent += SerializationServices_OnDeserializedEvent;
        SerializationServices.OnDeserializedFailedEvent += SerializationServices_OnDeserializedFailedEvent;

        string someXml = string.Empty; //replace this with something...
        MyDesrializedClass deserializedClass = someXml.Deserialize<MyDesrializedClass>();
    }

    private static void SerializationServices_OnDeserializedFailedEvent(string xmlSource)
    {
        //will get here before 'deserializedClass' will get it's results
    }

    private static void SerializationServices_OnDeserializedEvent(object itemDeserialized, string xmlSource)
    {
        //will get here before 'deserializedClass' will get it's results
    }
}

, если вы вставите этот кодв разные пространства имен не забудьте добавить 'using ...', иначе вы не увидите метод десериализации в контексте программы

Tzahi

...