Deserialize into existing objects using a standard formatter

    Regular deserialization .net always creates a graph of new objects. This is not always convenient.

    • For example, if objects contain non-serializable data, open handles, and more.
    • Objects that do not fall into serialization may have links to objects to be read, etc. This is especially true if your assembly is used by someone else, and you cannot solve all such cases with the right design.
    • And in the end, for the sake of a small Undo, completely re-creating objects is irrational.


    The search did not give a ready answer. There are not the easiest solutions using protobuf and other third-party serializers, but this is not always applicable.

    The task as a whole is not difficult, and my solution is not something outstanding, but on the other hand, for those who are faced with a similar problem for the first time, it will be easier.



    Serialization is done as usual. The following 2 classes will solve the problem of deserialization.

    	
    	[Serializable]
    	publicclassRealObjectHelper : IObjectReference, ISerializable 
    	{
    		Object m_realObject;
    		virtualobjectgetObject(ObjectId id)
    		{
    			//Этот метод должен возвращать ваш объект,return id.GetObject();
    		}
    		publicRealObjectHelper(SerializationInfo info, StreamingContext context)
    		{
    			ObjectId id = (ObjectId)info.GetValue("ID", typeof(ObjectId));
    			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;
    				} elseif(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());
    		}
    		publicobjectGetRealObject( StreamingContext context )
    		{
    			return m_realObject;
    		}
    		[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
    		publicvoidGetObjectData(SerializationInfo info, StreamingContext context)
    		{
    		}
    	}
    	publicclassRealObjectBinder: SerializationBinder
    	{
    		String assemVer;
    		String typeVer;
    		publicRealObjectBinder(String asmName, String typeName)
    		{
    			assemVer = asmName;
    			typeVer = typeName;
    		}
    		publicoverride Type BindToType( String assemblyName, String typeName ) 
    		{
    			Type typeToDeserialize = null;
    			if ( assemblyName.Equals( assemVer ) && typeName.Equals( typeVer ) )
    			{
    				returntypeof(RealObjectHelper);
    			}
    			typeToDeserialize = Type.GetType( String.Format(  "{0}, {1}", typeName, assemblyName ) );
    			return typeToDeserialize;
    		}
    	}
    


    When deserializing, you need to install Binder, which will create a wrapper for deserialization in your existing object.

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

    Also popular now: