【问题标题】:C# DataContract Serialization, how to deserialize to already existing instanceC# DataContract 序列化,如何反序列化到已经存在的实例
【发布时间】:2010-12-28 20:01:06
【问题描述】:

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

基本上是这样的:

[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 被序列化。当反序列化这种类型的实例时,我想获取已创建为静态字段的实例(ABC),使用Foo.Get(id) 而不是获取新实例。

有没有简单的方法来做到这一点?我没有找到任何我能够理解的资源。

【问题讨论】:

    标签: c# serialization netdatacontractserializer


    【解决方案1】:

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

    我不确定合同的确切签名。我使用了SerializableAttribute,并用它看起来smth。像这样:

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

    【讨论】:

    • 其实我不想关注 using 类,因为Foo 是一个非常核心的类,并且被许多其他类型使用。
    • 这个实现会是什么样子?
    • 好的,看起来很有希望。这是否也意味着我需要在所有课程中使用FooSerializableWrapper 而不是Foo?这将非常糟糕......或者我可以以某种方式插入包装器吗?
    • 你需要在Foo需要序列化/反序列化的任何地方使用FooSerializableWrappe
    【解决方案2】:

    使用OnDeserializingAttribute,您可能会朝着您正在寻找的东西迈出一步。但是,这可能只会让您设置属性(因此您可以拥有相当于使用静态实例填充当前实例的所有属性的 Copy 方法。

    我认为,如果您真的想返回静态实例,您可能必须编写自己的反序列化器...

    未经测试,但我认为您可以像这样轻松实现反序列化器:

    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 序列化;用您实际使用的任何内容替换继承。最后,这意味着您必须在反序列化对象时实例化这种类型,这可能涉及更改一些代码和/或配置。

    【讨论】:

    • 我知道OnDeserializing 属性。它不允许创建实例,因为它是在已经创建的实例上调用的。还有ISerializable接口和其他东西,但是我找不到解决方案。
    • 查看有关构建您自己的反序列化器的更多信息。
    • 我需要它用于 WCF (NetDataContractSerializer)。我可以为整个应用程序使用另一个序列化程序,但我想尽可能地保留基础架构。
    【解决方案3】:

    在反序列化过程中 (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

    【讨论】:

      【解决方案4】:

      没问题,只需使用 2 个类。在 getObject 方法中,您可以获取现有对象

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

      【讨论】:

        猜你喜欢
        • 2012-06-23
        • 1970-01-01
        • 1970-01-01
        • 2016-02-09
        • 1970-01-01
        • 1970-01-01
        • 2023-03-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多