public static void WriteTypeMetaInfo(IStreamWriter writer, ITypeStructure typeItem, bool writeTypeMetaPrefix = true) { if (writeTypeMetaPrefix) { writer.WriteUInt8(ValueItem.TypeMetaInfo); } writer.WriteUInt8(SingleTypeDescr); Type type = typeItem.RuntimeType; writer.WriteString(type.Assembly.GetName().Name); writer.WriteString(type.FullName); short itemCount = (short)typeItem.Items.Count; writer.WriteInt16(itemCount); for (int i = 0; i < itemCount; i++) { var memberItem = typeItem.Items[i]; writer.WriteInt16((short)memberItem.Type); // ItemType Enum writer.WriteUInt32(memberItem.TypeId); // TypeId (Type Hash) writer.WriteString(memberItem.Name); // Property Name writer.WriteBool(memberItem.IsNullable); // Nullable Flag } }
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!"); } }