Diserialize в существующие объекты используя стандартный форматер

от автора

Штатная десериализация .net всегда создает граф новых объектов. Это не всегда удобно.

  • Например если объекты содержат несериализуемые данные, открытые хэндлы и прочее.
  • Объекты не попадающие в сериализацию могут иметь ссылки на зачитываемые объекты и т.п. Особенно это актуально, если ваша сборка используется еще кем то, и вы не можете решить все подобные случаи при помощи правильного дизайна.
  • И в конце концов ради небольшого Undo полностью пересоздавать объекты нерационально.

Поиск не дал готового ответа. Есть не самые простые решения с использованием protobuf и прочих сторонних сериализаторов, но это не всегда применимо.

Задача в целом несложная, и мое решение не является чем то выдающимся, но с другой стороны, тем кто впервые столкнется с похожей проблемой — будет проще.

Сериализация делается как обычно. Следующие 2 класса решат проблему при десериализации.

	 	[Serializable] 	public class RealObjectHelper : IObjectReference, ISerializable  	{ 		Object m_realObject; 		virtual object getObject(ObjectId id) 		{ 			//Этот метод должен возвращать ваш объект, 			return id.GetObject(); 		} 		public RealObjectHelper(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; 				} 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 RealObjectBinder: SerializationBinder 	{ 		String assemVer; 		String typeVer; 		public RealObjectBinder(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(RealObjectHelper); 			} 			typeToDeserialize = Type.GetType( String.Format(  "{0}, {1}", typeName, assemblyName ) ); 			return typeToDeserialize; 		} 	} 

При десерализации надо установить Binder, который создаст обертку для десериализации в ваш существующий объект.

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

ссылка на оригинал статьи http://habrahabr.ru/post/159855/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *