如何反序列化为现有对象 – C#

在C#中,在将对象序列化到文件之后,如何在不创建新对象的情况下将文件反序列化为现有对象?

我可以找到的自定义序列化的所有示例都涉及实现一个将在反序列化时调用的构造函数,这正是我想要的,除了我不希望函数成为构造函数。

谢谢!

一些序列化程序支持回调; 例如, BinaryFormatterDataContractSerializer (以及下面的protobuf-net)允许您指定before-serializaton回调,并且由于它们跳过构造函数,这可能足以初始化对象。 但序列化程序仍在创建它。


大多数序列化程序都想要自己创建新对象,但有些序列化程序允许您反序列化为现有对象。 嗯,实际上唯一一个跳跃到脑海的是protobuf-net(披露:我是作者)……

这有两个不同的function,可能会有所帮助; 对于对象(即图中最外面的对象),您可以将现有对象直接提供给Merge方法(在v1中,在v2中也为了兼容性),或者在(在v2中)提供Deserialize方法; 例如:

 var obj = Serializer.Merge(source, instance); 

但是,在较大的图形中,您可能希望自己提供其他对象(而不仅仅是根目录)。 以下内容未在属性 API中公开,但是是v2中的新function:

 RuntimeTypeModel.Default[typeof(SomeType)].SetFactory(factoryMethod); 

其中factoryMethod可以是SomeType中的static方法的SomeType (返回SomeType实例),也可以是任何地方的任何static方法的MethodInfo 。 如果需要,该方法还可以(可选)将序列化上下文作为参数。 然后,此方法应用于提供SomeType所有新实例。


注意:protobuf-net与BinaryFormatter 完全相同; 为了获得最佳效果,您需要告诉它如何映射您的成员 – 非常类似于将事物标记为WCF / DataContractSerializer的[DataMember] 。 这可以是属性,但不是必须的。

没问题,只需使用2个class级。 在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 deserializeMembers = new List(members.Length); List data = new List(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); 

如果只是复制几个字段的问题,我会避免所有麻烦并采取简单的路径 – 反序列化为新实例,然后将适当的字段复制到现有实例。 这将花费你几个额外的副本,但你将节省大量的时间进行调试。

这有点不寻常,但它有效:

 [Serializable] public class Pets { public int Cats { get; set; } public int Dogs { get; set; } } public static class Utils { public static byte[] BinarySerialize(object o) { using (var ms = new MemoryStream()) { IFormatter f = new BinaryFormatter(); f.Serialize(ms, o); return ms.ToArray(); } } public static dynamic BinaryDeserialize(byte[] bytes, dynamic o) { using (var ms = new MemoryStream(bytes)) { ms.Position = 0; IFormatter f = new BinaryFormatter(); o = (dynamic)f.Deserialize(ms); return o; } } } 

对于不能通过引用传递的动态,反序列化很棘手。 最后是一个无意义的certificate测试:

 Pets p = new Pets() { Cats = 0, Dogs = 3 }; Console.WriteLine("{0}, {1}", p.Cats, p.Dogs); byte[] serial = Utils.BinarySerialize(p); p.Cats = 1; Console.WriteLine("{0}, {1}", p.Cats, p.Dogs); p = Utils.BinaryDeserialize(serial, p); Console.WriteLine("{0}, {1}", p.Cats, p.Dogs); 

输出如下:

 0, 3 1, 3 0, 3 

用文件流替换内存字符串,您将得到相同的结果。