public override void WriteValue(IStreamWriter writer, ISerializeContext context, object value) { // Write ItemType.TypeRef ID //writer.WriteUInt32(this.TypeId); if (value == null) { writer.WriteUInt8(ValueItem.NullValueIdent); } else { Type typeRef = (Type)value; IValueItem item = context.GetByType(typeRef); bool typeMetaInfo = false; if (item is ITypeStructure) { ITypeStructure tStructure = (ITypeStructure)item; if (context.IsWriteTypeMetaInfoRequired(item.TypeId)) { writer.WriteUInt8(ValueItem.TypeMetaInfo); typeMetaInfo = true; } } if (!typeMetaInfo) { writer.WriteUInt8(ValueItem.SingleValueIdent); } writer.WriteUInt32(item.TypeId); if (typeMetaInfo) { // serialize type meta info TypeMetaStructure.WriteTypeMetaInfo(writer, (ITypeStructure)item, false); } } }
internal static IValueItem ReadTypeMetaInfo(IStreamReader reader, uint typeId, ISerializeContext ctx) { byte metaTypeVersion = reader.ReadUInt8(); string assemblyName; string typeFullName; Type type; if (metaTypeVersion == SingleTypeDescr) { assemblyName = reader.ReadString(); typeFullName = reader.ReadString(); if (!TypeService.TryGetTypeByName(typeFullName, out type, ctx.Serializer.CustomLookupAssemblies)) { if (ctx.Serializer.AutoCreateMissingTypes) { return(AutoCreateMissingDeserializeType(typeId, assemblyName, typeFullName, reader, ctx)); } else { throw new TypeAccessException($"Type {typeFullName} not found!"); } } IValueItem resultItem = ctx.GetByType(type); if (resultItem is ITypeStructure) { ITypeStructure result = (ITypeStructure)resultItem; short itemCount = reader.ReadInt16(); if (result.TypeId == typeId) { // internal type is equal remote type for (int i = 0; i < itemCount; i++) { SkipPropertyMetaInfo(reader); } return(result); } else { // differences between local type and remote type // read actual structure and try map to old/new structure ComplexStructure tolerantLayoutStructure = ComplexStructure.CreateTolerantLayoutStructure(type, ctx); for (int i = 0; i < itemCount; i++) { ItemType itemType = (ItemType)reader.ReadInt16(); // ItemType Enum uint propertyTypeId = reader.ReadUInt32(); // TypeId (Type Hash) string propertyName = reader.ReadString(); // Property Name bool isNullable = reader.ReadBool(); // Nullable Flag IValueItem nameMatchingItem = result.Items.Where(item => item.Name == propertyName).FirstOrDefault(); if (nameMatchingItem != null) { bool typeMatch = itemType == nameMatchingItem.Type; bool typeIdMatch = propertyTypeId == nameMatchingItem.TypeId; bool nullableMatch = isNullable == nameMatchingItem.IsNullable; bool fromNotNullable = !isNullable && nameMatchingItem.IsNullable; if (typeMatch && typeIdMatch && nullableMatch) { // Property has exactly the same attributes > no conversion required tolerantLayoutStructure.AddTolerantLayoutProperty(ctx, propertyName); } else if (typeMatch && typeIdMatch && !nullableMatch && fromNotNullable) { // Property changed only from not nullable to nullable tolerantLayoutStructure.AddTolerantLayoutConverterProperty(nameMatchingItem, ctx, propertyName, itemType, propertyTypeId, isNullable, v => v); } else if (!typeMatch && !typeIdMatch && (nullableMatch || fromNotNullable)) { // Only property type changed // Check if type can be converted without loosing data Func <object, object> converter = null; if (itemType == ItemType.Int32 && nameMatchingItem.Type == ItemType.Int64) { // Read incoming Int32 and convert to local Int64 property converter = v => (long)(int)v; } else if (itemType == ItemType.Int16 && nameMatchingItem.Type == ItemType.Int32) { // Read incoming Int16 and convert to local Int32 property converter = v => (int)(short)v; } else if (itemType == ItemType.Byte && nameMatchingItem.Type == ItemType.Int16) { // Read incoming Byte and convert to local Int16 property converter = v => (short)(byte)v; } else if (itemType == ItemType.Double && nameMatchingItem.Type == ItemType.Decimal) { // Read incoming Double and convert to local Decimal property converter = v => (decimal)(double)v; } else if (itemType == ItemType.Char && nameMatchingItem.Type == ItemType.String) { // Read incoming Char and convert to local String property converter = v => new string(new char[] { (char)v }); } if (converter != null) { tolerantLayoutStructure.AddTolerantLayoutConverterProperty(nameMatchingItem, ctx, propertyName, itemType, propertyTypeId, isNullable, converter); } } } else { // no name matching property found (removed, renamed or old local version) > create dummy item tolerantLayoutStructure.AddTolerantLayoutDummyProperty(ctx, propertyName, itemType, propertyTypeId, isNullable); } } tolerantLayoutStructure.FinalizeTolerantLayoutStructure(); if (typeId == tolerantLayoutStructure.TypeId) { // Tolerant mapping was successful // Register only type ID in global cache BinarySerializer.RegisterTolerantTypeMapping(tolerantLayoutStructure); return(tolerantLayoutStructure); } else { // Can't map old/new structure to incoming type meta data throw new FormatException($"The incoming binary format does not match the loaded type {type.FullName}! An tolerant mapping was not possible. Please update the remote or local interface assemblies. Expected Type Id: {result.TypeId}; Received Type Id: {typeId}"); } } } else if (resultItem is ITypePrefix) { // Only type prefix - e.g. enum ITypePrefix result = (ITypePrefix)resultItem; short itemCount = reader.ReadInt16(); if (result.TypeId == typeId) { // internal type is equal remote type for (int i = 0; i < itemCount; i++) { SkipPropertyMetaInfo(reader); } return(result); } else { throw new FormatException($"The incoming binary format does not match the loaded type {type.FullName}! An tolerant mapping was not possible. Please update the remote or local interface assemblies. Expected Type Id: {result.TypeId}; Received Type Id: {typeId}"); } } else { return(resultItem); } } else if (metaTypeVersion == CollectionTypeDescr) { assemblyName = reader.ReadString(); typeFullName = reader.ReadString(); //todo: load assembly if required if (TypeService.TryGetTypeByName(typeFullName, out type)) { CollectionItems result = (CollectionItems)ctx.GetByType(type); if (result.TypeId == typeId) { return(result); } else { // differences between local type and remote type //todo: implement throw new NotImplementedException($"Support for different binary layout is not implemented yet! Local Type ID: {result.TypeId}; Remote Type ID: {typeId}"); } } else { throw new TypeAccessException($"Type {typeFullName} not found!"); } } else { throw new NotSupportedException($"Type meta information version {metaTypeVersion} not supported!"); } }
public void WriteValue(IStreamWriter writer, ISerializeContext context, object value) { // Complex structure // TypeID UInt32 // ContentType Byte context.Key = this.Name; if (isObject) { // property or collection does not specify a target type // determine object type if (value != null) { Type valType = value.GetType(); IValueItem item; var differentTargetStructure = context.DetermineSpecialInterfaceType(value.GetType(), type); if (differentTargetStructure != null) { item = differentTargetStructure; } else { item = context.GetByType(valType); } if (!item.IsTypePrefixExpected) { // Write type id for value types as well (unknown structure) writer.WriteUInt32(item.TypeId); writer.WriteUInt8(ValueItem.SingleValueIdent); } item.WriteValue(writer, context, value); } else { // write complex object type id for null value writer.WriteInt32((int)ItemType.ComplexObject); writer.WriteUInt8(ValueItem.NullValueIdent); } } else if (value == null) { // Write null writer.WriteUInt32(this.TypeId); writer.WriteUInt8(ValueItem.NullValueIdent); } else { IValueItem differentTargetStructure = null; if (CheckDifferentType) { differentTargetStructure = context.DetermineSpecialInterfaceType(value.GetType(), type); } if (differentTargetStructure != null) { // Special target interface type serialization differentTargetStructure.WriteValue(writer, context, value); } else { // Just write type properties writer.WriteUInt32(this.TypeId); if (context.IsWriteTypeMetaInfoRequired(this.TypeId)) { // serialize type meta info at the first time TypeMetaStructure.WriteTypeMetaInfo(writer, this); } writer.WriteUInt8(ValueItem.SingleObjectIdent); object oldParentObj = context.ParentObject; context.ParentObject = value; for (int itemIndex = 0; itemIndex < items.Count; itemIndex++) { var item = items[itemIndex]; var valueItem = item.GetItemValue(value); item.WriteValue(writer, context, valueItem); } context.ParentObject = oldParentObj; } } }