如何通过XML序列化来了解何时加载?

时间:2021-10-27 08:51:05

I'm trying to load a tree of objects via XML serialisation, and at the moment it will load the objects in, and create the tree quite happily. My issue revolves around the fact that these classes support a level of auditing. What I'd like to be able to do is call some method on each object after it has finished being loaded.

我正在尝试通过XML序列化加载一个对象树,此时它将加载对象,并非常愉快地创建树。我的问题围绕着这些类支持一定级别的审计这一事实。我希望能够做的是在每个对象加载完成后调用一些方法。

For the sake of argument, assume I have a fairly generic object tree with differing classes at different levels, like:

为了论证,假设我有一个相当通用的对象树,在不同的级别有不同的类,如:

 <Customer name="Foo Bar Inc.">
   <Office IsHq="True">
     <Street>123 Any Street</Street>
     <Town name="Anytown">
       <State name="Anystate">
         <Country name="My Country" />
       </State>
     </Town>
   </Office>
   <Office IsHq="False">
     <Street>456 High Street</Street>
     <Town name="Anycity">
       <State name="Anystate">
         <Country name="My Country" />
       </State>
     </Town>
   </Office>
 </Customer>

Is there any way using the default serialisers (In the similar way that you can create methods like ShouldSerializeFoo) to determine when loading has finished for each object?

有没有办法使用默认的序列化器(以类似的方式创建像ShouldSerializeFoo这样的方法)来确定每个对象的加载何时完成?

Edit: I should point out that the obvious case of exposing something akin to an OnLoaded() method that I could call after deserialising, strikes me as being a "bad thing to do".

编辑:我应该指出,暴露类似于反序列化后我可以调用的OnLoaded()方法的明显案例,让我觉得这是一件“坏事”。

Edit2: For the sake of discussion this is my current hack "approach", which works for the basic level, but the child City node still thinks it needs to be saved with changes (in the real world the object model is a lot more complex, but this will at least compile, without the need for full source)

Edit2:为了讨论起见,这是我当前的hack“方法”,它适用于基本级别,但子City节点仍然认为需要保存更改(在现实世界中,对象模型要复杂得多) ,但这至少会编译,而不需要完整的源代码)

public class Office
{
    [XmlAttribute("IsHq")]
    public bool IsHeadquarters { get; set; }

    [XmlElement]
    public string Street { get; set; }

    [XmlElement]
    public Town Town { get; set; }

    protected virtual void OnLoaded() {}

    public static OfficeCollection Search()
    {
        OfficeCollection retval = new OfficeCollection();
        string xmlString = @"
                    <Office IsHq='True'>
                        <Street>123 Any Street</Street>
                        <Town name='Anytown'>
                            <State name='Anystate'>
                                <Country name='My Country' />
                            </State>
                        </Town>
                    </Office>";

        XmlSerializer xs = new XmlSerializer(retval.GetType());
        XmlReader xr = new XmlTextReader(xmlString);
        retval = (OfficeCollection)xs.Deserialize(xr);

        foreach (Office thisOffice in retval)
        {
            thisOffice.OnLoaded();
        }
        return retval;
    }
}

9 个解决方案

#1


14  

Hmmm... it's still not pretty but you could refactor your deserialization logic into a dedicated class which could notify the deserialized object that it originated from XML before returning it to the caller.

嗯......它仍然不是很漂亮,但你可以将你的反序列化逻辑重构为一个专用类,它可以在将反序列化对象返回给调用者之前通知反序列化对象它来自XML。

Update: I think this should be fairly easy to do without straying too far from the patterns laid by the framework... you'd just need to ensure that you use the CustomXmlSerializer. Classes that need this notification just need to implement IXmlDeserializationCallback

更新:我认为这应该是相当容易做到的,不会偏离框架所设置的模式......你只需要确保使用CustomXmlSerializer。需要此通知的类只需要实现IXmlDeserializationCallback

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;
        }
    }
}

#2


4  

The accepted solution didn't quite work for me. The overridden Deserialize() method never got called. I believe this is because that method is not public and is therefore called by one (or more) of the public Deserialize() methods, but not all of them.

接受的解决方案对我来说并不适用。重写的Deserialize()方法永远不会被调用。我相信这是因为该方法不公开,因此被一个(或多个)公共Deserialize()方法调用,但不是全部。

Here's an implementation that works by method hiding and makes use of the existing IDeserializationCallback interface so any deserialization using non-xml methods can still trigger the OnDeserialization() method of that interface. It also uses reflection to traverse child properties to see if they also implement IDeserializationCallback and calls them accordingly.

这是一个通过方法隐藏工作的实现,并利用现有的IDeserializationCallback接口,因此使用非xml方法的任何反序列化仍然可以触发该接口的OnDeserialization()方法。它还使用反射来遍历子属性,以查看它们是否也实现了IDeserializationCallback并相应地调用它们。

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;

namespace Xml.Serialization
{
    class XmlCallbackSerializer : XmlSerializer
    {
        public XmlCallbackSerializer(Type type) : base(type)
        {
        }

        public XmlCallbackSerializer(XmlTypeMapping xmlTypeMapping) : base(xmlTypeMapping)
        {
        }

        public XmlCallbackSerializer(Type type, string defaultNamespace) : base(type, defaultNamespace)
        {
        }

        public XmlCallbackSerializer(Type type, Type[] extraTypes) : base(type, extraTypes)
        {
        }

        public XmlCallbackSerializer(Type type, XmlAttributeOverrides overrides) : base(type, overrides)
        {
        }

        public XmlCallbackSerializer(Type type, XmlRootAttribute root) : base(type, root)
        {
        }

        public XmlCallbackSerializer(Type type, XmlAttributeOverrides overrides, Type[] extraTypes,
            XmlRootAttribute root, string defaultNamespace) : base(type, overrides, extraTypes, root, defaultNamespace)
        {
        }

        public XmlCallbackSerializer(Type type, XmlAttributeOverrides overrides, Type[] extraTypes,
            XmlRootAttribute root, string defaultNamespace, string location)
            : base(type, overrides, extraTypes, root, defaultNamespace, location)
        {
        }

        public new object Deserialize(Stream stream)
        {
            var result = base.Deserialize(stream);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        public new object Deserialize(TextReader textReader)
        {
            var result = base.Deserialize(textReader);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        public new object Deserialize(XmlReader xmlReader)
        {
            var result = base.Deserialize(xmlReader);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        public new object Deserialize(XmlSerializationReader reader)
        {
            var result = base.Deserialize(reader);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        public new object Deserialize(XmlReader xmlReader, string encodingStyle)
        {
            var result = base.Deserialize(xmlReader, encodingStyle);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        public new object Deserialize(XmlReader xmlReader, XmlDeserializationEvents events)
        {
            var result = base.Deserialize(xmlReader, events);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        public new object Deserialize(XmlReader xmlReader, string encodingStyle, XmlDeserializationEvents events)
        {
            var result = base.Deserialize(xmlReader, encodingStyle, events);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        private void CheckForDeserializationCallbacks(object deserializedObject)
        {
            var deserializationCallback = deserializedObject as IDeserializationCallback;

            if (deserializationCallback != null)
            {
                deserializationCallback.OnDeserialization(this);
            }

            var properties = deserializedObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (var propertyInfo in properties)
            {
                if (propertyInfo.PropertyType.GetInterface(typeof(IEnumerable<>).FullName) != null)
                {
                    var collection = propertyInfo.GetValue(deserializedObject) as IEnumerable;

                    if (collection != null)
                    {
                        foreach (var item in collection)
                        {
                            CheckForDeserializationCallbacks(item);
                        }
                    }
                }
                else
                {
                    CheckForDeserializationCallbacks(propertyInfo.GetValue(deserializedObject));
                }
            }
        }
    }
}

#3


2  

I tried the solution provided by abatishchev but as pointed out by the comments below his answer, the Deserialize method in the custom serializer never seems to get called.

我尝试了abatishchev提供的解决方案,但正如他的回答下面的评论所指出的,自定义序列化器中的Deserialize方法似乎永远不会被调用。

I was able to get this working by overloading all the different Deserialize overloads I would need so that it would always call the custom method.

我能够通过重载我需要的所有不同的Deserialize重载来使这个工作,以便它总是调用自定义方法。

protected object Deserialize(System.IO.StringReader reader)
{
    var result = base.Deserialize(reader);

    CallBack(result);

    return result;
}

protected object Deserialize(System.IO.TextReader reader)
{
    var result = base.Deserialize(reader);

    CallBack(result);

    return result;
}

protected object Deserialize(System.Xml.XmlReader reader)
{
    var result = base.Deserialize(reader);

    CallBack(result);

    return result;
}

protected object Deserialize(System.IO.Stream stream)
{
    var result = base.Deserialize(stream);

    CallBack(result);

    return result;
}

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

This way I actually see the Deserialize method getting called.

这样我实际上看到了Deserialize方法被调用。

#4


1  

A toughie, since XmlSerializer doesn't support serialization callback events. Is there any way you could use DataContractSerializer? That does, but doesn't allow attributes (like @name above).

很难,因为XmlSerializer不支持序列化回调事件。有什么办法可以使用DataContractSerializer吗?这样做,但不允许属性(如上面的@name)。

Otherwise; you could implement IXmlSerializable, but that is lots of work, and very error-prone.

除此以外;你可以实现IXmlSerializable,但这是很多工作,而且非常容易出错。

Otherwise - perhaps checking the caller via the stack, but that is very brittle, and smells ripe.

否则 - 也许通过堆栈检查呼叫者,但这非常脆弱,并且闻起来很成熟。

#5


1  

After wasting some time with first answer, I adopted code from HotN's post, except for CheckForDeserializationCallbacks:

在第一次回答后浪费了一些时间后,我从HotN的帖子中采用了代码,但CheckForDeserializationCallbacks除外:

private static void ProcessOnDeserialize(object _result) {
  var type = _result != null ? _result.GetType() : null;
  var methods = type != null ? type.GetMethods().Where(_ => _.GetCustomAttributes(true).Any(_m => _m is OnDeserializedAttribute)) : null;
  if (methods != null) {
    foreach (var mi in methods) {
      mi.Invoke(_result, null);
    }
  }
  var properties = type != null ? type.GetProperties().Where(_ => _.GetCustomAttributes(true).Any(_m => _m is XmlElementAttribute || _m is XmlAttributeAttribute)) : null;
  if (properties != null) {
    foreach (var prop in properties) {
      var obj = prop.GetValue(_result, null);
      var enumeration = obj as IEnumerable;
      if (obj is IEnumerable) {
        foreach (var item in enumeration) {
          ProcessOnDeserialize(item);
        }
      } else {
        ProcessOnDeserialize(obj);
      }
    }
  }
}

This allows using of standard [OnDeserialized].

这允许使用标准[OnDeserialized]。

UPD. Updated post for recursive walk on object tree.

UPD。更新了对象树上的递归遍历的帖子。

#6


0  

I use a factory method which add more logic after the XML structured object has been deserialized. Such logic includes restoring the internal relationship (child-parent, sibling..) between object members.

我使用一个工厂方法,在反序列化XML结构化对象后添加更多逻辑。这种逻辑包括恢复对象成员之间的内部关系(子父,兄弟......)。

#7


0  

In my case it was a collection of objects, so used an excepted solution having to modify it a bit

在我的情况下,它是一个对象的集合,所以使用一个例外的解决方案,必须稍微修改它

  private static void PostDeserializedProcess<T>(T deserializedObj)
    {
        var deserializedCallback = deserializedObj as IXmlPostDeserializationCallback;
        if (deserializedCallback != null)
        {
            deserializedCallback.OnXmlDeserialized(deserializedObj);
        }
        else
        {
            // it could be a List of objects 
            // and we need to check for every object in the list
            var collection = deserializedObj as System.Collections.IEnumerable;
            if (collection != null)
            {
                foreach (var item in collection)
                {
                    PostDeserializedProcess(item);
                }
            }
        }
    }

And then everything is working perfectly

然后一切都很完美

#8


0  

I struggled somewhat as well getting the above solutions to work. I found the simplest solution to have my OnDeserialization() callbacks fire while using XmlSerializer was to chain a call to BinaryFormatter afterwards. My class already had a GetClone() method so it was rather straightforward and negated all my attempts at overriding XmlSerializer

我努力工作以获得上述解决方案。我找到了最简单的解决方案,让我的OnDeserialization()回调激发,而使用XmlSerializer后来将链接调用BinaryFormatter。我的类已经有了一个GetClone()方法,所以它很简单,并且否定了我重写XmlSerializer的所有尝试

public static Foo Deserialize(string path) {
    Foo foo;
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(Foo));
    using (StreamReader textReader = new StreamReader(path)) {
        foo = (Foo)xmlSerializer.Deserialize(textReader); // this does NOT fire the OnDeserialization callbacks
    }
    return foo.GetClone();
}

public Foo GetClone() {
    using (var ms = new MemoryStream()) {
        var formatter = new BinaryFormatter();
        formatter.Serialize(ms, this);
        ms.Position = 0;
        return (Foo)formatter.Deserialize(ms); // this DOES fire the OnDeserialization callbacks
    }
}

#9


0  

The accepted solution didn't work for me either.

接受的解决方案对我来说也不起作用。

In order to make it finally work, I needed to modify HotN's solution a little bit. In particular I added the

为了使它最终起作用,我需要稍微修改HotN的解决方案。特别是我加了

propertyInfo.GetIndexParameters().Length

check (according to: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.propertyinfo.getvalue), in order to avoid a missmatching parameters exception.

检查(根据:https://docs.microsoft.com/en-us/dotnet/api/system.reflection.propertyinfo.getvalue),以避免错配参数异常。

Also I have some properties that are not supposed to get mapped. I attributed them with [XmlIgnore], but the provided solution still processed them. But adding a check check for whether the passed parameter object is null did the trick.

此外,我有一些不应该映射的属性。我将它们归结为[XmlIgnore],但提供的解决方案仍然处理它们。但是添加一个检查,检查传递的参数对象是否为空。

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

    public class CustomXmlSerializer : XmlSerializer
    {
        public CustomXmlSerializer(Type type) : base(type) { }

        public new object Deserialize(Stream stream)
        {
            var result = base.Deserialize(stream);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        private void CheckForDeserializationCallbacks(object deserializedObject)
        {
            if (deserializedObject == null)
                return;

            var deserializationCallback = deserializedObject as IXmlDeserializationCallback;

            if (deserializationCallback != null)
            {
                deserializationCallback.OnXmlDeserialization(this);
            }

            var properties = deserializedObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (var propertyInfo in properties)
            {

                if (propertyInfo.PropertyType.GetInterface(typeof(IEnumerable<>).FullName) != null)
                {
                    var collection = propertyInfo.GetValue(deserializedObject) as IEnumerable;

                    if (collection != null)
                    {
                        foreach (var item in collection)
                        {
                            CheckForDeserializationCallbacks(item);
                        }
                    }
                }
                else
                {
                    if (propertyInfo.GetIndexParameters().Length == 0)
                        CheckForDeserializationCallbacks(propertyInfo.GetValue(deserializedObject));
                }
            }
        }
    }
}

#1


14  

Hmmm... it's still not pretty but you could refactor your deserialization logic into a dedicated class which could notify the deserialized object that it originated from XML before returning it to the caller.

嗯......它仍然不是很漂亮,但你可以将你的反序列化逻辑重构为一个专用类,它可以在将反序列化对象返回给调用者之前通知反序列化对象它来自XML。

Update: I think this should be fairly easy to do without straying too far from the patterns laid by the framework... you'd just need to ensure that you use the CustomXmlSerializer. Classes that need this notification just need to implement IXmlDeserializationCallback

更新:我认为这应该是相当容易做到的,不会偏离框架所设置的模式......你只需要确保使用CustomXmlSerializer。需要此通知的类只需要实现IXmlDeserializationCallback

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;
        }
    }
}

#2


4  

The accepted solution didn't quite work for me. The overridden Deserialize() method never got called. I believe this is because that method is not public and is therefore called by one (or more) of the public Deserialize() methods, but not all of them.

接受的解决方案对我来说并不适用。重写的Deserialize()方法永远不会被调用。我相信这是因为该方法不公开,因此被一个(或多个)公共Deserialize()方法调用,但不是全部。

Here's an implementation that works by method hiding and makes use of the existing IDeserializationCallback interface so any deserialization using non-xml methods can still trigger the OnDeserialization() method of that interface. It also uses reflection to traverse child properties to see if they also implement IDeserializationCallback and calls them accordingly.

这是一个通过方法隐藏工作的实现,并利用现有的IDeserializationCallback接口,因此使用非xml方法的任何反序列化仍然可以触发该接口的OnDeserialization()方法。它还使用反射来遍历子属性,以查看它们是否也实现了IDeserializationCallback并相应地调用它们。

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;

namespace Xml.Serialization
{
    class XmlCallbackSerializer : XmlSerializer
    {
        public XmlCallbackSerializer(Type type) : base(type)
        {
        }

        public XmlCallbackSerializer(XmlTypeMapping xmlTypeMapping) : base(xmlTypeMapping)
        {
        }

        public XmlCallbackSerializer(Type type, string defaultNamespace) : base(type, defaultNamespace)
        {
        }

        public XmlCallbackSerializer(Type type, Type[] extraTypes) : base(type, extraTypes)
        {
        }

        public XmlCallbackSerializer(Type type, XmlAttributeOverrides overrides) : base(type, overrides)
        {
        }

        public XmlCallbackSerializer(Type type, XmlRootAttribute root) : base(type, root)
        {
        }

        public XmlCallbackSerializer(Type type, XmlAttributeOverrides overrides, Type[] extraTypes,
            XmlRootAttribute root, string defaultNamespace) : base(type, overrides, extraTypes, root, defaultNamespace)
        {
        }

        public XmlCallbackSerializer(Type type, XmlAttributeOverrides overrides, Type[] extraTypes,
            XmlRootAttribute root, string defaultNamespace, string location)
            : base(type, overrides, extraTypes, root, defaultNamespace, location)
        {
        }

        public new object Deserialize(Stream stream)
        {
            var result = base.Deserialize(stream);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        public new object Deserialize(TextReader textReader)
        {
            var result = base.Deserialize(textReader);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        public new object Deserialize(XmlReader xmlReader)
        {
            var result = base.Deserialize(xmlReader);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        public new object Deserialize(XmlSerializationReader reader)
        {
            var result = base.Deserialize(reader);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        public new object Deserialize(XmlReader xmlReader, string encodingStyle)
        {
            var result = base.Deserialize(xmlReader, encodingStyle);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        public new object Deserialize(XmlReader xmlReader, XmlDeserializationEvents events)
        {
            var result = base.Deserialize(xmlReader, events);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        public new object Deserialize(XmlReader xmlReader, string encodingStyle, XmlDeserializationEvents events)
        {
            var result = base.Deserialize(xmlReader, encodingStyle, events);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        private void CheckForDeserializationCallbacks(object deserializedObject)
        {
            var deserializationCallback = deserializedObject as IDeserializationCallback;

            if (deserializationCallback != null)
            {
                deserializationCallback.OnDeserialization(this);
            }

            var properties = deserializedObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (var propertyInfo in properties)
            {
                if (propertyInfo.PropertyType.GetInterface(typeof(IEnumerable<>).FullName) != null)
                {
                    var collection = propertyInfo.GetValue(deserializedObject) as IEnumerable;

                    if (collection != null)
                    {
                        foreach (var item in collection)
                        {
                            CheckForDeserializationCallbacks(item);
                        }
                    }
                }
                else
                {
                    CheckForDeserializationCallbacks(propertyInfo.GetValue(deserializedObject));
                }
            }
        }
    }
}

#3


2  

I tried the solution provided by abatishchev but as pointed out by the comments below his answer, the Deserialize method in the custom serializer never seems to get called.

我尝试了abatishchev提供的解决方案,但正如他的回答下面的评论所指出的,自定义序列化器中的Deserialize方法似乎永远不会被调用。

I was able to get this working by overloading all the different Deserialize overloads I would need so that it would always call the custom method.

我能够通过重载我需要的所有不同的Deserialize重载来使这个工作,以便它总是调用自定义方法。

protected object Deserialize(System.IO.StringReader reader)
{
    var result = base.Deserialize(reader);

    CallBack(result);

    return result;
}

protected object Deserialize(System.IO.TextReader reader)
{
    var result = base.Deserialize(reader);

    CallBack(result);

    return result;
}

protected object Deserialize(System.Xml.XmlReader reader)
{
    var result = base.Deserialize(reader);

    CallBack(result);

    return result;
}

protected object Deserialize(System.IO.Stream stream)
{
    var result = base.Deserialize(stream);

    CallBack(result);

    return result;
}

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

This way I actually see the Deserialize method getting called.

这样我实际上看到了Deserialize方法被调用。

#4


1  

A toughie, since XmlSerializer doesn't support serialization callback events. Is there any way you could use DataContractSerializer? That does, but doesn't allow attributes (like @name above).

很难,因为XmlSerializer不支持序列化回调事件。有什么办法可以使用DataContractSerializer吗?这样做,但不允许属性(如上面的@name)。

Otherwise; you could implement IXmlSerializable, but that is lots of work, and very error-prone.

除此以外;你可以实现IXmlSerializable,但这是很多工作,而且非常容易出错。

Otherwise - perhaps checking the caller via the stack, but that is very brittle, and smells ripe.

否则 - 也许通过堆栈检查呼叫者,但这非常脆弱,并且闻起来很成熟。

#5


1  

After wasting some time with first answer, I adopted code from HotN's post, except for CheckForDeserializationCallbacks:

在第一次回答后浪费了一些时间后,我从HotN的帖子中采用了代码,但CheckForDeserializationCallbacks除外:

private static void ProcessOnDeserialize(object _result) {
  var type = _result != null ? _result.GetType() : null;
  var methods = type != null ? type.GetMethods().Where(_ => _.GetCustomAttributes(true).Any(_m => _m is OnDeserializedAttribute)) : null;
  if (methods != null) {
    foreach (var mi in methods) {
      mi.Invoke(_result, null);
    }
  }
  var properties = type != null ? type.GetProperties().Where(_ => _.GetCustomAttributes(true).Any(_m => _m is XmlElementAttribute || _m is XmlAttributeAttribute)) : null;
  if (properties != null) {
    foreach (var prop in properties) {
      var obj = prop.GetValue(_result, null);
      var enumeration = obj as IEnumerable;
      if (obj is IEnumerable) {
        foreach (var item in enumeration) {
          ProcessOnDeserialize(item);
        }
      } else {
        ProcessOnDeserialize(obj);
      }
    }
  }
}

This allows using of standard [OnDeserialized].

这允许使用标准[OnDeserialized]。

UPD. Updated post for recursive walk on object tree.

UPD。更新了对象树上的递归遍历的帖子。

#6


0  

I use a factory method which add more logic after the XML structured object has been deserialized. Such logic includes restoring the internal relationship (child-parent, sibling..) between object members.

我使用一个工厂方法,在反序列化XML结构化对象后添加更多逻辑。这种逻辑包括恢复对象成员之间的内部关系(子父,兄弟......)。

#7


0  

In my case it was a collection of objects, so used an excepted solution having to modify it a bit

在我的情况下,它是一个对象的集合,所以使用一个例外的解决方案,必须稍微修改它

  private static void PostDeserializedProcess<T>(T deserializedObj)
    {
        var deserializedCallback = deserializedObj as IXmlPostDeserializationCallback;
        if (deserializedCallback != null)
        {
            deserializedCallback.OnXmlDeserialized(deserializedObj);
        }
        else
        {
            // it could be a List of objects 
            // and we need to check for every object in the list
            var collection = deserializedObj as System.Collections.IEnumerable;
            if (collection != null)
            {
                foreach (var item in collection)
                {
                    PostDeserializedProcess(item);
                }
            }
        }
    }

And then everything is working perfectly

然后一切都很完美

#8


0  

I struggled somewhat as well getting the above solutions to work. I found the simplest solution to have my OnDeserialization() callbacks fire while using XmlSerializer was to chain a call to BinaryFormatter afterwards. My class already had a GetClone() method so it was rather straightforward and negated all my attempts at overriding XmlSerializer

我努力工作以获得上述解决方案。我找到了最简单的解决方案,让我的OnDeserialization()回调激发,而使用XmlSerializer后来将链接调用BinaryFormatter。我的类已经有了一个GetClone()方法,所以它很简单,并且否定了我重写XmlSerializer的所有尝试

public static Foo Deserialize(string path) {
    Foo foo;
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(Foo));
    using (StreamReader textReader = new StreamReader(path)) {
        foo = (Foo)xmlSerializer.Deserialize(textReader); // this does NOT fire the OnDeserialization callbacks
    }
    return foo.GetClone();
}

public Foo GetClone() {
    using (var ms = new MemoryStream()) {
        var formatter = new BinaryFormatter();
        formatter.Serialize(ms, this);
        ms.Position = 0;
        return (Foo)formatter.Deserialize(ms); // this DOES fire the OnDeserialization callbacks
    }
}

#9


0  

The accepted solution didn't work for me either.

接受的解决方案对我来说也不起作用。

In order to make it finally work, I needed to modify HotN's solution a little bit. In particular I added the

为了使它最终起作用,我需要稍微修改HotN的解决方案。特别是我加了

propertyInfo.GetIndexParameters().Length

check (according to: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.propertyinfo.getvalue), in order to avoid a missmatching parameters exception.

检查(根据:https://docs.microsoft.com/en-us/dotnet/api/system.reflection.propertyinfo.getvalue),以避免错配参数异常。

Also I have some properties that are not supposed to get mapped. I attributed them with [XmlIgnore], but the provided solution still processed them. But adding a check check for whether the passed parameter object is null did the trick.

此外,我有一些不应该映射的属性。我将它们归结为[XmlIgnore],但提供的解决方案仍然处理它们。但是添加一个检查,检查传递的参数对象是否为空。

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

    public class CustomXmlSerializer : XmlSerializer
    {
        public CustomXmlSerializer(Type type) : base(type) { }

        public new object Deserialize(Stream stream)
        {
            var result = base.Deserialize(stream);

            CheckForDeserializationCallbacks(result);

            return result;
        }

        private void CheckForDeserializationCallbacks(object deserializedObject)
        {
            if (deserializedObject == null)
                return;

            var deserializationCallback = deserializedObject as IXmlDeserializationCallback;

            if (deserializationCallback != null)
            {
                deserializationCallback.OnXmlDeserialization(this);
            }

            var properties = deserializedObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (var propertyInfo in properties)
            {

                if (propertyInfo.PropertyType.GetInterface(typeof(IEnumerable<>).FullName) != null)
                {
                    var collection = propertyInfo.GetValue(deserializedObject) as IEnumerable;

                    if (collection != null)
                    {
                        foreach (var item in collection)
                        {
                            CheckForDeserializationCallbacks(item);
                        }
                    }
                }
                else
                {
                    if (propertyInfo.GetIndexParameters().Length == 0)
                        CheckForDeserializationCallbacks(propertyInfo.GetValue(deserializedObject));
                }
            }
        }
    }
}