• DataContractSerializer数据不一致下序列化


       一、数据类型的等效性

             例如下面定义的两个类成员名称、定义顺序都不一样,但是在DataContract、DataMember的Name属性作用下,两个类的实例对象序列化后的xml是一样的,因此Order和OrderV2对于DataContractSerializer序列化是等效的。

       [DataContract]
        public class Order
        {
            [DataMember]
            public int price;
    
            [DataMember(Name = "datev2")]
            public DateTime date;
        }
    
        [DataContract(Name = "Order")]
        public class OrderV2
        {
            [DataMember]
            public DateTime datev2;
    
            [DataMember(Name = "price")]
            public int PriceV2;
        }
    

     二、数据添加导致的不一致

         假设客户端定义的是OrderLess,服务端定义的是OrderMore。客户端会基于OrderLess进行序列化后发送到服务端。服务端会基于OrderMore反序列化来自客户端的OrderLess序列化后的消息,能序列化成功,但是会发现缺少Name的值。

     [DataContract(Name="Order")]
        public class OrderLess
        {
            [DataMember]
            public int Price;
    
            [DataMember]
            public DateTime Date;   
    } [DataContract(Name = "Order")] public class OrderMore { [DataMember] public DateTime Date; [DataMember] public int Price; [DataMember] public string Name; }

      此时如果我们想对这个缺失的值设置一个默认值,可以通过注册序列化回调方法实现。  

       OnDeserialized  :  在对象反序列化后立即调用。

       OnDeserializing :反序列化对象之前调用。

       OnSerialized     :  在序列化后调用此。

       OnSerializing    :在序列化前调用。

    对OrderMore修改后如下:

    [DataContract(Name = "Order")]
        public class OrderMore
        {
            [DataMember]
            public DateTime Date;
    
            [DataMember]
            public int Price;
    
            [DataMember]
            public string Name;
    
            [OnDeserializing]
            void OnSerializing(StreamingContext context)
            {
               this.Name="未指定";
            }
        }
    

     Main函数:

     static void Main(string[] args)
            {
               OrderLess less=new OrderLess();
               less.Date=DateTime.Now;
               less.Price=8;
               Serialize<OrderLess>(less,@"D:1.txt");
               OrderMore more=null;
               DeSerialize<OrderMore>(@"D:1.txt",out more);
               Console.WriteLine("{0} {1} {2}",more.Date,more.Price,more.Name);
            }
            public static void Serialize<T>(T t, string path)
            {
                DataContractSerializer ser = new DataContractSerializer(typeof(T));
                using (XmlTextWriter writer = new XmlTextWriter(path, Encoding.UTF8))
                {
                    writer.Formatting = Formatting.Indented;
                    ser.WriteObject(writer, t);
                }
            }
            public static void DeSerialize<T>(string path, out T t)
            {
                DataContractSerializer ser = new DataContractSerializer(typeof(T));
                using (XmlReader reader = new XmlTextReader(path))
                {
                    t = (T)ser.ReadObject(reader);
                }
            }
    

    输出:

    三、数据删除导致的不一致

       与二中的交换一下,客户端定义的是OrderMore,服务端定义的是OrderLess。客户端基于OrderMore类型的序列化器序列化OrderMore对象,服务端按基于OrderLess的序列化器反序列化来自客户端的OrderMore的消息。服务端也能序列化成功,但是当服务端将这个对象返回给客户端时,客户端那边会发现少了Name值。为了避免这种情况发生,OrderLess可以实现IExtensibleDataObject接口。作用是将反序列化时未知的数据存放到ExtensionData,再次对这个对象序列化时就可以还原这些未知对象。

    对OrderLess进行修改:

       [DataContract(Name="Order")]
        public class OrderLess:IExtensibleDataObject
        {
            [DataMember]
            public int Price;
    
            [DataMember]
            public DateTime Date;
    
            public ExtensionDataObject ExtensionData
            {
                get;set;
            }
        }
    

     Main函数模拟客户端和服务端的交互过程,先序列化OrderMore,再反序列化为OrderLess,此时将未知的Name属性放在OrderLess的ExtensionData。再次序列化这个OrderLess对象。发现1.txt和2.txt的序列化内容是一样的。

     static void Main(string[] args)
            {
                OrderMore more = new OrderMore();
                more.Date = DateTime.Now;
                more.Price = 8;
                more.Name = "OrderMore";
                Serialize<OrderMore>(more, @"D:1.txt");
    
                OrderLess less = null;
                DeSerialize<OrderLess>(@"D:1.txt", out less);
                Serialize<OrderLess>(less, @"D:2.txt");
            }
    

     四、数据契约代理

       在上述描述都是数据成员添加或删除这种小差异。当两个对象差异很大时(如下面的A和B),却想让两个对象序列化时等效,需要数据契约代理来做一些工作。

    [DataContract]
        public class A
        {
            [DataMember]
            public string FullName;
    
            [DataMember]
            public int age;
        }
    
        [DataContract]
        public class B
        {
            [DataMember]
            public string  FirstName;
    
            [DataMember]
            public string LastName;
    
            [DataMember]
            public int age;
        }
    

     MatchA2B实现IDataContractSurrogate接口,在DataContractSerializer序列化过程中能够将一个A对象序列化为B对象,将B对象反序列化为A对象。需要实现GetDataContractType方法,获取需要序列化的实际类型。GetObjectToSerialize序列化前的转换,GetDeserializedObject序列化后的转换。

    public class MatchA2B : IDataContractSurrogate
        {
            public Type GetDataContractType(Type type)
            {
                return type == typeof(A) ? typeof(B) : type;
            }
            public object GetObjectToSerialize(object obj, Type targetType)
            {
                A a=obj as A;
                if (a != null)
                {
                    B b = new B();
                    b.age = a.age;
                    b.FirstName = a.FullName.Split(" ".ToCharArray())[0];
                    b.LastName = a.FullName.Split(" ".ToCharArray())[1];
                    return b;
                }
                return obj;
            }
    
            public object GetDeserializedObject(object obj, Type targetType)
            {
                B b = obj as B;
                if (b != null)
                {
                    A a = new A();
                    a.FullName = b.FirstName + " " + b.LastName;
                    a.age = b.age;
                    return a;
                }
                return obj;
            }
            public object GetCustomDataToExport(Type clrType, Type dataContractType){return null;}
            public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType){return null;}
            public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes){ }
            public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData){return null;}
            public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit) 
            { return null; }
        }
    

     Main函数:

    class Program
        {
            static void Main(string[] args)
            {
                A a = new A();
                a.FullName = "Leonardo DiCaprio";
                a.age = 41;
                Serialize<A>(a, @"D:1.txt");
    
                A b = null;
                DeSerialize<A>(@"D:1.txt", out b);
                Console.WriteLine("{0} {1} ", b.FullName, b.age);
            }
            public static void Serialize<T>(T t, string path)
            {
                DataContractSerializer ser = new DataContractSerializer(typeof(T),null,int.MaxValue,false,false,new MatchA2B());
                using (XmlTextWriter writer = new XmlTextWriter(path, Encoding.UTF8))
                {
                    writer.Formatting = Formatting.Indented;
                    ser.WriteObject(writer, t);
                }
            }
            public static void DeSerialize<T>(string path, out T t)
            {
                DataContractSerializer ser = new DataContractSerializer(typeof(T), null, int.MaxValue, false, false, new MatchA2B());
                using (XmlReader reader = new XmlTextReader(path))
                {
                    t = (T)ser.ReadObject(reader);
                }
            }
        }
    

    1.txt中的内容为,成功的将A对象序列化为B:

    <B xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication3">
      <FirstName>Leonardo</FirstName>
      <LastName>DiCaprio</LastName>
      <age>41</age>
    </B>
    
  • 相关阅读:
    maven常用命令
    div标签width:auto无效
    将本地文件推送到码云
    Spring事件监听讲解
    常用js代码积累
    HTML中块级元素和行内元素的总结和区分
    box-shadow详解
    设置最小宽高的作用
    Java英语词汇表
    标识符
  • 原文地址:https://www.cnblogs.com/lh218/p/4510179.html
Copyright © 2020-2023  润新知