代码之家  ›  专栏  ›  技术社区  ›  Stefan Steinegger

c datacontract序列化,如何反序列化到已有实例

  •  8
  • Stefan Steinegger  · 技术社区  · 15 年前

    我有一个类,它包含所有现有实例的静态字典,这些实例是在编译时定义的。

    基本上是这样的:

    [DataContract]
    class Foo
    {
      private static Dictionary<long, Foo> instances = new Dictionary<long, Foo>();
    
      [DataMember]
      private long id;
    
      public static readonly Foo A = Create(1);
      public static readonly Foo B = Create(2);
      public static readonly Foo C = Create(3);
    
      private static Foo Create(long id)
      {
        Foo instance = new Foo();
        instance.id = id;
        instances.Add(instance);
        return instance;
      }
    
      public static Foo Get(long id)
      {
        return instances[id];
      }    
    
    }
    

    还有其他字段,类是派生的,但这与问题无关。

    只有 id 已序列化。当反序列化此类型的实例时,我希望获取已创建为静态字段的实例( A , B C )使用 Foo.Get(id) 而不是得到一个新的实例。

    有什么简单的方法可以做到这一点吗?我没有找到任何我能理解的资源。

    4 回复  |  直到 15 年前
        1
  •  17
  •   Marc Gravell    15 年前

    在反序列化期间,它(afaik)总是使用一个新对象( FormatterServices.GetUninitializedObject ,但要让它替换对象 之后 反序列化(但在它们返回给调用方之前),可以实现 IObjectReference ,像这样:

    [DataContract]
    class Foo : IObjectReference { // <===== implement an extra interface
        object IObjectReference.GetRealObject(StreamingContext ctx) {
            return Get(id);
        }
        ...snip
    }
    

    完成。。。证明:

    static class Program {
        static void Main() {
            Foo foo = Foo.Get(2), clone;
            DataContractSerializer ser = new DataContractSerializer(typeof(Foo));
            using (MemoryStream ms = new MemoryStream()) { // clone it via DCS
                ser.WriteObject(ms, foo);
                ms.Position = 0;
                clone = (Foo)ser.ReadObject(ms);
            }
            Console.WriteLine(ReferenceEquals(foo, clone)); // true
        }
    }
    

    注意,对于msdn上的部分信任场景,这里有一些额外的说明, here .

        2
  •  3
  •   ironic    15 年前

    我有一个类似的问题,我发现最好的解决方案是添加一些包装类,即管理需要序列化的包装类的实例。

    我不确定合同上的确切签名。我用了serializableattribute,用它我看起来很渺小。像这样:

    [Serializable]
    class FooSerializableWrapper : ISerializable
    {
        private readonly long id;
    
        public Foo Foo
        {
            get
            {
                return Foo.Get(id);
            }
        }
    
        public FooSerializableWrapper(Foo foo)
        {
            id = foo.id;
        }
    
        protected FooSerializableWrapper(SerializationInfo info, StreamingContext context)
        {
            id = info.GetInt64("id");
        }
    
    
        void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("id", id);
        }
    
    }
    
        3
  •  0
  •   Chris Shaffer    15 年前

    你也许能朝着你想要的用途迈出一步 OnDeserializingAttribute . 但是,这可能只允许您设置属性(因此您可以拥有相当于使用静态实例填充当前实例的所有属性的复制方法)。

    我想如果你真的想返回你的静态实例,你可能需要编写你自己的反序列化程序…

    未经测试,但我假设您可以很容易地实现如下反序列化程序:

    public class MyDeserializer : System.Xml.Serialization.XmlSerializer
    {
        protected override object Deserialize(System.Xml.Serialization.XmlSerializationReader reader)
        {
            Foo obj = (Foo)base.Deserialize(reader);
            return Foo.Get(obj.id);
        }
    }
    

    请注意,由于id在代码中是私有的,因此您必须对其进行一些处理;这也假设您使用的是xml序列化;将继承替换为实际使用的任何内容。最后,这意味着在反序列化对象时必须实例化此类型,这可能涉及更改某些代码和/或配置。

        4
  •  0
  •   Alexander    12 年前

    没问题,只用两个班。在get object方法中,您可以获取现有的对象

    [Serializable]
    public class McRealObjectHelper : IObjectReference, ISerializable 
    {
        Object m_realObject;
        virtual object getObject(McObjectId id)
        {
            return id.GetObject();
        }
        public McRealObjectHelper(SerializationInfo info, StreamingContext context)
        {
            McObjectId id = (McObjectId)info.GetValue("ID", typeof(McObjectId));
            m_realObject = getObject(id);
            if(m_realObject == null)
                return;
            Type t = m_realObject.GetType();
            MemberInfo[] members = FormatterServices.GetSerializableMembers(t, context);
            List<MemberInfo> deserializeMembers = new List<MemberInfo>(members.Length);
            List<object> data = new List<object>(members.Length);
            foreach(MemberInfo mi in members)
            {
                Type dataType = null;
                if(mi.MemberType == MemberTypes.Field)
                {
                    FieldInfo fi = mi as FieldInfo;
                    dataType = fi.FieldType;
                } else if(mi.MemberType == MemberTypes.Property){
                    PropertyInfo pi = mi as PropertyInfo;
                    dataType = pi.PropertyType;
                }
                try
                {
                    if(dataType != null){
                        data.Add(info.GetValue(mi.Name, dataType));
                        deserializeMembers.Add(mi);
                    }
                }
                catch (SerializationException)
                {
                    //some fiels are missing, new version, skip this fields
                }
            }
            FormatterServices.PopulateObjectMembers(m_realObject, deserializeMembers.ToArray(), data.ToArray());
        }
    
        public object GetRealObject( StreamingContext context )
        {
            return m_realObject;
        }
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
        }
    }
    
    public class McRealObjectBinder: SerializationBinder
    {
        String assemVer;
        String typeVer;
        public McRealObjectBinder(String asmName, String typeName)
        {
            assemVer = asmName;
            typeVer = typeName;
        }
        public override Type BindToType( String assemblyName, String typeName ) 
        {
            Type typeToDeserialize = null;
            if ( assemblyName.Equals( assemVer ) && typeName.Equals( typeVer ) )
            {
                return typeof(McRealObjectHelper);
            }
            typeToDeserialize = Type.GetType( String.Format(  "{0}, {1}", typeName, assemblyName ) );
            return typeToDeserialize;
        }
    }
    

    然后,当反序列化时:

    BinaryFormatter bf = new BinaryFormatter(null, context);
    bf.Binder = new McRealObjectBinder(YourType.Assembly.FullName, YourType.FullName);
    bf.Deserialize(memStream);