代码之家  ›  专栏  ›  技术社区  ›  Rowland Shaw

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

  •  14
  • Rowland Shaw  · 技术社区  · 16 年前

    我尝试通过XML序列化加载一个对象树,现在它将加载对象,并非常高兴地创建树。我的问题围绕着这样一个事实:这些类支持某种程度的审计。我想能够做的是在加载完每个对象之后对其调用一些方法。

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

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

    是否有任何方法可以使用默认的序列化程序(与创建类似的方法,如 ShouldSerializeFoo )确定每个对象的加载何时完成?

    编辑: 我应该指出,暴露类似于 OnLoaded() 我的方法 能够 在反序列化之后调用,我觉得这是一件“不好的事情”。

    编辑2: 为了讨论,这是我目前的情况 乱劈 “方法”,在基本层面上是有效的,但是子城市节点仍然认为它需要随着变化而保存(在现实世界中,对象模型要复杂得多,但这至少需要编译,而不需要完整的源代码)

    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 回复  |  直到 7 年前
        1
  •  14
  •   abatishchev Karl Johan    14 年前

    六羟甲基三聚氰胺六甲醚。。。它仍然不漂亮,但您可以将反序列化逻辑重构为一个专用类,该类可以在将其返回给调用方之前通知反序列化对象它来自XML。

    更新: 我认为这应该很容易做到,而不会偏离框架所建立的模式太远…您只需要确保使用customXmlSerializer。需要此通知的类只需要实现IXML反序列化回调

    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
  •   HotN    10 年前

    接受的解决方案对我来说不太管用。被重写的 Deserialize() 从未调用方法。我认为这是因为该方法不是公共方法,因此由一个(或多个)公共方法调用 反序列化() 方法,但不是所有方法。

    下面是一个通过方法隐藏工作的实现,它利用现有的 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
  •   Freek Buurman    13 年前

    我尝试了阿巴蒂什切夫提供的解决方案,但正如下面的评论所指出的,他的答案是 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);
        }
    }
    

    这样我就能看到 反序列化 方法被调用。

        4
  •  1
  •   Marc Gravell    16 年前

    一种疼痛,因为 XmlSerializer 不支持序列化回调事件。你能用什么方法吗 DataContractSerializer ?那 ,但不允许属性(如 @name 以上)。

    否则,您可以实现 IXmlSerializable ,但那是 太多了 很容易出错。

    否则-可能通过堆栈检查调用者,但这是 非常 易碎,闻起来很熟。

        5
  •  1
  •   Norritt    9 年前

    在第一个答案浪费了一段时间后,我从霍顿的帖子中采用了代码,除了 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);
          }
        }
      }
    }
    

    这允许使用标准 [OnDeserialized] .

    UPD。 更新了在对象树上递归行走的日志。

        6
  •  0
  •   Khanh Hua    12 年前

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

        7
  •  0
  •   Anastasia Melnikova    9 年前

    在我的例子中,它是一个对象集合,因此使用了一个例外的解决方案,必须对其进行一点修改。

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

    然后一切都很顺利

        8
  •  0
  •   user2921789    8 年前

    我也在努力使上述解决方案发挥作用。我找到了最简单的解决方案 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
  •   ltsstar    7 年前

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

    为了使它最终工作,我需要修改霍顿的解决方案一点。 特别是我添加了

    propertyInfo.GetIndexParameters().Length
    

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

    另外,我还有一些不应该映射的属性。我将它们归因于[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));
                    }
                }
            }
        }
    }
    
    推荐文章