C#DataContract序列化,如何反序列化到已存在的实例

时间:2009-12-21 17:50:07

标签: c# serialization netdatacontractserializer

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

基本上它看起来像这样:

[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获取已创建为静态字段(BCFoo.Get(id))的实例而不是获得新的实例。

有一种简单的方法吗?我没有找到任何我能够理解的资源。

4 个答案:

答案 0 :(得分:17)

在反序列化期间,它(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,有一些额外的注意事项。

答案 1 :(得分:3)

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

我不确定合同的确切签名。我使用了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);
    }

}

答案 2 :(得分:0)

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

答案 3 :(得分:0)

没问题,只需使用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);