/// <summary> /// Writes the given <see cref="SerializationInfo" /> using the given writer. /// </summary> /// <param name="info">The <see cref="SerializationInfo" /> to write.</param> /// <param name="writer">The writer to use.</param> private void WriteSerializationInfo(SerializationInfo info, IDataWriter writer) { try { writer.BeginArrayNode(info.MemberCount); foreach (var entry in info) { try { writer.WriteString("type", writer.Context.Binder.BindToName(entry.ObjectType, writer.Context.Config.DebugContext)); var readerWriter = Serializer.Get(entry.ObjectType); readerWriter.WriteValueWeak(entry.Name, entry.Value, writer); } catch (Exception ex) { writer.Context.Config.DebugContext.LogException(ex); } } } finally { writer.EndArrayNode(); } }
/// <summary> /// Deserializes a value of a given type from the given reader, using the given list of Unity objects for external index reference resolution. /// </summary> /// <typeparam name="T">The type to deserialize.</typeparam> /// <param name="reader">The reader to use.</param> /// <param name="referencedUnityObjects">The list of Unity objects to use for external index reference resolution.</param> /// <returns> /// The deserialized value. /// </returns> public static T DeserializeValue <T>(IDataReader reader, List <UnityEngine.Object> referencedUnityObjects) { using (var unityResolver = Cache <UnityReferenceResolver> .Claim()) { unityResolver.Value.SetReferencedUnityObjects(referencedUnityObjects); reader.Context.IndexReferenceResolver = unityResolver.Value; return(Serializer.Get <T>().ReadValue(reader)); } }
/// <summary> /// Serializes the given value, using the given writer. /// </summary> /// <typeparam name="T">The type of the value to serialize.</typeparam> /// <param name="value">The value to serialize.</param> /// <param name="writer">The writer to use.</param> /// <param name="unityObjects">A list of the Unity objects which were referenced during serialization.</param> public static void SerializeValue <T>(T value, IDataWriter writer, out List <UnityEngine.Object> unityObjects) { using (var unityResolver = Cache <UnityReferenceResolver> .Claim()) { writer.Context.IndexReferenceResolver = unityResolver.Value; Serializer.Get <T>().WriteValue(value, writer); writer.FlushToStream(); unityObjects = unityResolver.Value.GetReferencedUnityObjects(); } }
/// <summary> /// Creates and reads into a <see cref="SerializationInfo" /> instance using a given reader and context. /// </summary> /// <param name="reader">The reader to use.</param> /// <returns> /// The <see cref="SerializationInfo" /> which was read. /// </returns> private SerializationInfo ReadSerializationInfo(IDataReader reader) { string name; EntryType entry = reader.PeekEntry(out name); if (entry == EntryType.StartOfArray) { try { long length; reader.EnterArray(out length); SerializationInfo info = new SerializationInfo(typeof(T), reader.Context.FormatterConverter); for (int i = 0; i < length; i++) { Type type = null; entry = reader.PeekEntry(out name); if (entry == EntryType.String && name == "type") { string typeName; reader.ReadString(out typeName); type = reader.Context.Binder.BindToType(typeName, reader.Context.Config.DebugContext); } if (type == null) { reader.SkipEntry(); continue; } entry = reader.PeekEntry(out name); var readerWriter = Serializer.Get(type); object value = readerWriter.ReadValueWeak(reader); info.AddValue(name, value); } return(info); } finally { reader.ExitArray(); } } return(null); }
/// <summary> /// Provides the actual implementation for deserializing a value of type <see cref="T" />. /// </summary> /// <param name="value">The uninitialized value to serialize into. This value will have been created earlier using <see cref="BaseFormatter{T}.GetUninitializedObject" />.</param> /// <param name="reader">The reader to deserialize with.</param> protected override void DeserializeImplementation(ref T value, IDataReader reader) { // We sadly *must* box so that complex value types get their values properly set by reflection. // At least we only box these once. object boxedValue = value; var members = FormatterUtilities.GetSerializableMembersMap(typeof(T), this.OverridePolicy ?? reader.Context.Config.SerializationPolicy); EntryType entryType; string name; while ((entryType = reader.PeekEntry(out name)) != EntryType.EndOfNode && entryType != EntryType.EndOfArray && entryType != EntryType.EndOfStream) { if (string.IsNullOrEmpty(name)) { reader.Context.Config.DebugContext.LogError("Entry of type \"" + entryType + "\" in node \"" + reader.CurrentNodeName + "\" is missing a name."); reader.SkipEntry(); continue; } MemberInfo member; if (members.TryGetValue(name, out member) == false) { reader.Context.Config.DebugContext.LogWarning("Lost serialization data for entry \"" + name + "\" of type \"" + entryType + "\"in node \"" + reader.CurrentNodeName + "\"."); reader.SkipEntry(); continue; } Type expectedType = FormatterUtilities.GetContainedType(member); try { var serializer = Serializer.Get(expectedType); object entryValue = serializer.ReadValueWeak(reader); FormatterUtilities.SetMemberValue(member, boxedValue, entryValue); } catch (Exception ex) { reader.Context.Config.DebugContext.LogException(ex); } } value = (T)boxedValue; // Unbox }
/// <summary> /// Provides the actual implementation for serializing a value of type <see cref="T" />. /// </summary> /// <param name="value">The value to serialize.</param> /// <param name="writer">The writer to serialize with.</param> protected override void SerializeImplementation(ref T value, IDataWriter writer) { var members = FormatterUtilities.GetSerializableMembers(typeof(T), this.OverridePolicy ?? writer.Context.Config.SerializationPolicy); for (int i = 0; i < members.Length; i++) { var member = members[i]; Type type; var memberValue = FormatterUtilities.GetMemberValue(member, value); type = FormatterUtilities.GetContainedType(member); var serializer = Serializer.Get(type); try { serializer.WriteValueWeak(member.Name, memberValue, writer); } catch (Exception ex) { writer.Context.Config.DebugContext.LogException(ex); } } }
/// <summary> /// Reads a value of type <see cref="T" />. /// </summary> /// <param name="reader">The reader to use.</param> /// <returns> /// The value which has been read. /// </returns> public override T ReadValue(IDataReader reader) { var context = reader.Context; if (context.Config.SerializationPolicy.AllowNonSerializableTypes == false && TypeOf_T.IsSerializable == false) { context.Config.DebugContext.LogError("The type " + TypeOf_T.GetNiceFullName() + " is not marked as serializable."); return(default(T)); } bool exitNode = true; string name; var entry = reader.PeekEntry(out name); if (ComplexTypeIsValueType) { if (entry == EntryType.Null) { context.Config.DebugContext.LogWarning("Expecting complex struct of type " + TypeOf_T.GetNiceFullName() + " but got null value."); reader.ReadNull(); return(default(T)); } else if (entry != EntryType.StartOfNode) { context.Config.DebugContext.LogWarning("Unexpected entry '" + name + "' of type " + entry.ToString() + ", when " + EntryType.StartOfNode + " was expected. A value has likely been lost."); reader.SkipEntry(); return(default(T)); } try { Type expectedType = TypeOf_T; Type serializedType; if (reader.EnterNode(out serializedType)) { if (serializedType != expectedType) { if (serializedType != null) { context.Config.DebugContext.LogWarning("Expected complex struct value " + expectedType.GetNiceFullName() + " but the serialized value is of type " + serializedType.GetNiceFullName() + "."); if (serializedType.IsCastableTo(expectedType)) { object value = FormatterLocator.GetFormatter(serializedType, context.Config.SerializationPolicy).Deserialize(reader); bool serializedTypeIsNullable = serializedType.IsGenericType && serializedType.GetGenericTypeDefinition() == typeof(Nullable <>); bool allowCastMethod = !ComplexTypeIsNullable && !serializedTypeIsNullable; var castMethod = allowCastMethod ? serializedType.GetCastMethodDelegate(expectedType) : null; if (castMethod != null) { return((T)castMethod(value)); } else { return((T)value); } } else if (AllowDeserializeInvalidDataForT || reader.Context.Config.AllowDeserializeInvalidData) { context.Config.DebugContext.LogWarning("Can't cast serialized type " + serializedType.GetNiceFullName() + " into expected type " + expectedType.GetNiceFullName() + ". Attempting to deserialize with possibly invalid data. Value may be lost or corrupted for node '" + name + "'."); return(GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader)); } else { context.Config.DebugContext.LogWarning("Can't cast serialized type " + serializedType.GetNiceFullName() + " into expected type " + expectedType.GetNiceFullName() + ". Value lost for node '" + name + "'."); return(default(T)); } } else if (AllowDeserializeInvalidDataForT || reader.Context.Config.AllowDeserializeInvalidData) { context.Config.DebugContext.LogWarning("Expected complex struct value " + expectedType.GetNiceFullName() + " but the serialized type could not be resolved. Attempting to deserialize with possibly invalid data. Value may be lost or corrupted for node '" + name + "'."); return(GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader)); } else { context.Config.DebugContext.LogWarning("Expected complex struct value " + expectedType.GetNiceFullName() + " but the serialized type could not be resolved. Value lost for node '" + name + "'."); return(default(T)); } } else { return(GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader)); } } else { context.Config.DebugContext.LogError("Failed to enter node '" + name + "'."); return(default(T)); } } catch (SerializationAbortException ex) { exitNode = false; throw ex; } catch (Exception ex) { context.Config.DebugContext.LogException(ex); return(default(T)); } finally { if (exitNode) { reader.ExitNode(); } } } else { switch (entry) { case EntryType.Null: { reader.ReadNull(); return(default(T)); } case EntryType.ExternalReferenceByIndex: { int index; reader.ReadExternalReference(out index); object value = context.GetExternalObject(index); try { return((T)value); } catch (InvalidCastException) { context.Config.DebugContext.LogWarning("Can't cast external reference type " + value.GetType().GetNiceFullName() + " into expected type " + TypeOf_T.GetNiceFullName() + ". Value lost for node '" + name + "'."); return(default(T)); } } case EntryType.ExternalReferenceByGuid: { Guid guid; reader.ReadExternalReference(out guid); object value = context.GetExternalObject(guid); try { return((T)value); } catch (InvalidCastException) { context.Config.DebugContext.LogWarning("Can't cast external reference type " + value.GetType().GetNiceFullName() + " into expected type " + TypeOf_T.GetNiceFullName() + ". Value lost for node '" + name + "'."); return(default(T)); } } case EntryType.ExternalReferenceByString: { string id; reader.ReadExternalReference(out id); object value = context.GetExternalObject(id); try { return((T)value); } catch (InvalidCastException) { context.Config.DebugContext.LogWarning("Can't cast external reference type " + value.GetType().GetNiceFullName() + " into expected type " + TypeOf_T.GetNiceFullName() + ". Value lost for node '" + name + "'."); return(default(T)); } } case EntryType.InternalReference: { int id; reader.ReadInternalReference(out id); object value = context.GetInternalReference(id); try { return((T)value); } catch (InvalidCastException) { context.Config.DebugContext.LogWarning("Can't cast internal reference type " + value.GetType().GetNiceFullName() + " into expected type " + TypeOf_T.GetNiceFullName() + ". Value lost for node '" + name + "'."); return(default(T)); } } case EntryType.StartOfNode: { try { Type expectedType = TypeOf_T; Type serializedType; int id; if (reader.EnterNode(out serializedType)) { id = reader.CurrentNodeId; T result; if (serializedType != null && expectedType != serializedType) // We have type metadata different from the expected type { bool success = false; var isPrimitive = FormatterUtilities.IsPrimitiveType(serializedType); bool assignableCast; if (ComplexTypeMayBeBoxedValueType && isPrimitive) { // It's a boxed primitive type, so simply read that straight and register success var serializer = Serializer.Get(serializedType); result = (T)serializer.ReadValueWeak(reader); success = true; } else if ((assignableCast = expectedType.IsAssignableFrom(serializedType)) || serializedType.HasCastDefined(expectedType, false)) { try { object value; if (isPrimitive) { var serializer = Serializer.Get(serializedType); value = serializer.ReadValueWeak(reader); } else { var alternateFormatter = FormatterLocator.GetFormatter(serializedType, context.Config.SerializationPolicy); value = alternateFormatter.Deserialize(reader); } if (assignableCast) { result = (T)value; } else { var castMethod = serializedType.GetCastMethodDelegate(expectedType); if (castMethod != null) { result = (T)castMethod(value); } else { // Let's just give it a go anyways result = (T)value; } } success = true; } catch (SerializationAbortException ex) { exitNode = false; throw ex; } catch (InvalidCastException) { success = false; result = default(T); } } else if (!ComplexTypeIsAbstract && (AllowDeserializeInvalidDataForT || reader.Context.Config.AllowDeserializeInvalidData)) { // We will try to deserialize an instance of T with the invalid data. context.Config.DebugContext.LogWarning("Can't cast serialized type " + serializedType.GetNiceFullName() + " into expected type " + expectedType.GetNiceFullName() + ". Attempting to deserialize with invalid data. Value may be lost or corrupted for node '" + name + "'."); result = GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader); success = true; } else { // We couldn't cast or use the type, but we still have to deserialize it and register // the reference so the reference isn't lost if it is referred to further down // the data stream. var alternateFormatter = FormatterLocator.GetFormatter(serializedType, context.Config.SerializationPolicy); object value = alternateFormatter.Deserialize(reader); if (id >= 0) { context.RegisterInternalReference(id, value); } result = default(T); } if (!success) { // We can't use this context.Config.DebugContext.LogWarning("Can't cast serialized type " + serializedType.GetNiceFullName() + " into expected type " + expectedType.GetNiceFullName() + ". Value lost for node '" + name + "'."); result = default(T); } } else if (ComplexTypeIsAbstract) { result = default(T); } else { result = GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader); } if (id >= 0) { context.RegisterInternalReference(id, result); } return(result); } else { context.Config.DebugContext.LogError("Failed to enter node '" + name + "'."); return(default(T)); } } catch (SerializationAbortException ex) { exitNode = false; throw ex; } catch (Exception ex) { context.Config.DebugContext.LogException(ex); return(default(T)); } finally { if (exitNode) { reader.ExitNode(); } } } // // The below cases are for when we expect an object, but have // serialized a straight primitive type. In such cases, we can // often box the primitive type as an object. // // Sadly, the exact primitive type might be lost in case of // integer and floating points numbers, as we don't know what // type to expect. // // To be safe, we read and box the most precise type available. // case EntryType.Boolean: { if (!ComplexTypeMayBeBoxedValueType) { goto default; } bool value; reader.ReadBoolean(out value); return((T)(object)value); } case EntryType.FloatingPoint: { if (!ComplexTypeMayBeBoxedValueType) { goto default; } double value; reader.ReadDouble(out value); return((T)(object)value); } case EntryType.Integer: { if (!ComplexTypeMayBeBoxedValueType) { goto default; } long value; reader.ReadInt64(out value); return((T)(object)value); } case EntryType.String: { if (!ComplexTypeMayBeBoxedValueType) { goto default; } string value; reader.ReadString(out value); return((T)(object)value); } case EntryType.Guid: { if (!ComplexTypeMayBeBoxedValueType) { goto default; } Guid value; reader.ReadGuid(out value); return((T)(object)value); } default: // Lost value somehow context.Config.DebugContext.LogWarning("Unexpected entry of type " + entry.ToString() + ", when a reference or node start was expected. A value has been lost."); reader.SkipEntry(); return(default(T)); } } }
/// <summary> /// Writes a value of type <see cref="T" />. /// </summary> /// <param name="name">The name of the value to write.</param> /// <param name="value">The value to write.</param> /// <param name="writer">The writer to use.</param> public override void WriteValue(string name, T value, IDataWriter writer) { var context = writer.Context; var policy = context.Config.SerializationPolicy; if (policy.AllowNonSerializableTypes == false && TypeOf_T.IsSerializable == false) { context.Config.DebugContext.LogError("The type " + TypeOf_T.GetNiceFullName() + " is not marked as serializable."); return; } FireOnSerializedType(); if (ComplexTypeIsValueType) { bool endNode = true; try { writer.BeginStructNode(name, TypeOf_T); GetBaseFormatter(policy).Serialize(value, writer); } catch (SerializationAbortException ex) { endNode = false; throw ex; } finally { if (endNode) { writer.EndNode(name); } } } else { int id; int index; string strId; Guid guid; bool endNode = true; if (object.ReferenceEquals(value, null)) { writer.WriteNull(name); } else if (context.TryRegisterExternalReference(value, out index)) { writer.WriteExternalReference(name, index); } else if (context.TryRegisterExternalReference(value, out guid)) { writer.WriteExternalReference(name, guid); } else if (context.TryRegisterExternalReference(value, out strId)) { writer.WriteExternalReference(name, strId); } else if (context.TryRegisterInternalReference(value, out id)) { Type type = value.GetType(); // Get type of actual stored object if (ComplexTypeMayBeBoxedValueType && FormatterUtilities.IsPrimitiveType(type)) // It's a boxed primitive type { try { writer.BeginReferenceNode(name, type, id); var serializer = Serializer.Get(type); serializer.WriteValueWeak(value, writer); } catch (SerializationAbortException ex) { endNode = false; throw ex; } finally { if (endNode) { writer.EndNode(name); } } } else { IFormatter formatter; if (object.ReferenceEquals(type, TypeOf_T)) { formatter = GetBaseFormatter(policy); } else { formatter = FormatterLocator.GetFormatter(type, policy); } try { writer.BeginReferenceNode(name, type, id); formatter.Serialize(value, writer); } catch (SerializationAbortException ex) { endNode = false; throw ex; } finally { if (endNode) { writer.EndNode(name); } } } } else { writer.WriteInternalReference(name, id); } } }
/// <summary> /// Skips the next entry value, unless it is an <see cref="EntryType.EndOfNode"/> or an <see cref="EntryType.EndOfArray"/>. If the next entry value is an <see cref="EntryType.StartOfNode"/> or an <see cref="EntryType.StartOfArray"/>, all of its contents will be processed, deserialized and registered in the deserialization context, so that internal reference values are not lost to entries further down the stream. /// </summary> public virtual void SkipEntry() { var peekedEntry = this.PeekEntry(); if (peekedEntry == EntryType.StartOfNode) { Type type; bool exitNode = true; this.EnterNode(out type); try { if (type != null) { // We have the necessary metadata to read this type, and register all of its reference values (perhaps including itself) in the serialization context // Sadly, we have no choice but to risk boxing of structs here // Luckily, this is a rare case if (FormatterUtilities.IsPrimitiveType(type)) { // It is a boxed primitive type; we read the value and register it var serializer = Serializer.Get(type); object value = serializer.ReadValueWeak(this); if (this.CurrentNodeId >= 0) { this.Context.RegisterInternalReference(this.CurrentNodeId, value); } } else { var formatter = FormatterLocator.GetFormatter(type, this.Context.Config.SerializationPolicy); object value = formatter.Deserialize(this); if (this.CurrentNodeId >= 0) { this.Context.RegisterInternalReference(this.CurrentNodeId, value); } } } else { // We have no metadata, and reference values might be lost // We must read until a node on the same level terminates while (true) { peekedEntry = this.PeekEntry(); if (peekedEntry == EntryType.EndOfStream) { break; } else if (peekedEntry == EntryType.EndOfNode) { break; } else if (peekedEntry == EntryType.EndOfArray) { this.ReadToNextEntry(); // Consume end of arrays that we can potentially get stuck on } else { this.SkipEntry(); } } } } catch (SerializationAbortException ex) { exitNode = false; throw ex; } finally { if (exitNode) { this.ExitNode(); } } } else if (peekedEntry == EntryType.StartOfArray) { // We must read until an array on the same level terminates this.ReadToNextEntry(); // Consume start of array while (true) { peekedEntry = this.PeekEntry(); if (peekedEntry == EntryType.EndOfStream) { break; } else if (peekedEntry == EntryType.EndOfArray) { this.ReadToNextEntry(); // Consume end of array and break break; } else if (peekedEntry == EntryType.EndOfNode) { this.ReadToNextEntry(); // Consume end of nodes that we can potentially get stuck on } else { this.SkipEntry(); } } } else if (peekedEntry != EntryType.EndOfArray && peekedEntry != EntryType.EndOfNode) // We can't skip end of arrays and end of nodes { this.ReadToNextEntry(); // We can just skip a single value entry } }
/// <summary> /// Deserializes a value from the given reader. /// </summary> /// <typeparam name="T">The type to deserialize.</typeparam> /// <param name="reader">The reader to use.</param> /// <returns>The deserialized value.</returns> public static T DeserializeValue <T>(IDataReader reader) { return(Serializer.Get <T>().ReadValue(reader)); }
/// <summary> /// Deserializes a value from the given reader. This might fail with primitive values, as they don't come with metadata. /// </summary> /// <param name="reader">The reader to use.</param> /// <returns>The deserialized value.</returns> public static object DeserializeValueWeak(IDataReader reader) { return(Serializer.Get <object>().ReadValueWeak(reader)); }
/// <summary> /// Serializes the given value using the given writer. /// </summary> /// <typeparam name="T">The type of the value to serialize.</typeparam> /// <param name="value">The value to serialize.</param> /// <param name="writer">The writer to use.</param> public static void SerializeValue <T>(T value, IDataWriter writer) { Serializer.Get <T>().WriteValue(value, writer); writer.FlushToStream(); }
/// <summary> /// Gets a <see cref="Serializer"/> for type T. /// </summary> /// <typeparam name="T">The type to get a <see cref="Serializer"/> for.</typeparam> /// <returns>A <see cref="Serializer"/> for type T.</returns> public static Serializer <T> Get <T>() { return((Serializer <T>)Serializer.Get(typeof(T))); }