// called to find the correct handler for a polymorphic field internal SerializationHandler GetUntypedHandler(int handlerId, Type baseType) { // important: all code paths that could lead to the creation of a new handler need to lock. lock (this.syncRoot) { if (this.handlersById.TryGetValue(handlerId, out SerializationHandler handler)) { return(handler); } } if (this.schemasById.TryGetValue(handlerId, out TypeSchema schema)) { // do we have a type for this schema name? if (!this.knownTypes.TryGetValue(schema.Name, out Type type)) { // nothing registered, try getting a type based on the type hint type = TypeResolutionHelper.GetVerifiedType(schema.TypeName); if (type == null) { throw new SerializationException($"Failed to create a deserializer for type {schema.Name} because no type was registered for this name and the source type {schema.TypeName} could not be found. Add a reference to the assembly containing this type, or register an alternate type for this name."); } // register the type we found with schema name this.Register(type, schema.Name); } // this will create the handler if needed return(this.GetUntypedHandler(type)); } throw new SerializationException($"Could not find the appropriate derived type with serialization handler id={handlerId} when deserializing a polymorphic instance or field of type {baseType.AssemblyQualifiedName}"); }
/// <inheritdoc/> protected override void AddDerivedMemberStreamChildren() { // Get the type of this node. Type dataType = TypeResolutionHelper.GetVerifiedType(this.DataTypeName); // If this is already an auto-generated nullable, then the type we care to expand is // the value-type inside the nullable type. if (this.IsAutoGeneratedNullableMember) { dataType = dataType.GenericTypeArguments[0]; } // Determine if the current node is a reference type var isReference = this.IsAutoGeneratedNullableMember || !dataType.IsValueType || Nullable.GetUnderlyingType(dataType) != null; if (dataType != null) { // Add a child node for each public instance property that takes no parameters. foreach (PropertyInfo propertyInfo in dataType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(property => !property.GetMethod.GetParameters().Any())) { this.AddDerivedMemberStreamChild(propertyInfo, propertyInfo.PropertyType, isReference && propertyInfo.PropertyType.IsValueType); } // Add a child node for each public instance field foreach (FieldInfo fieldInfo in dataType.GetFields(BindingFlags.Public | BindingFlags.Instance)) { this.AddDerivedMemberStreamChild(fieldInfo, fieldInfo.FieldType, isReference && fieldInfo.FieldType.IsValueType); } } }
/// <inheritdoc/> protected override bool CanExpandDerivedMemberStreams() { // If we have already expanded this node with derived member streams if (this.InternalChildren.Any(c => c is DerivedMemberStreamTreeNode)) { // Then no longer expand return(false); } // Get the node type var nodeType = TypeResolutionHelper.GetVerifiedType(this.DataTypeName); // If it's an auto-generated nullable, we need to assess whether the inner value-type (inside the nullable) // can expand the members. if (this.IsAutoGeneratedNullableMember) { nodeType = nodeType.GenericTypeArguments[0]; } if (nodeType != null) { return(nodeType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(property => !property.GetMethod.GetParameters().Any()).Any() || nodeType.GetFields(BindingFlags.Public | BindingFlags.Instance).Any()); } return(false); }
/// <summary> /// Validate whether two schemas are compatible. /// </summary> /// <remarks>Schemas are compatible if all required fields are present in both (regardless of type).</remarks> /// <param name="other">Other type schema.</param> public void ValidateCompatibleWith(TypeSchema other) { if (this.IsPartial || other == null || other.IsPartial) { return; } if ((this.flags & TypeFlags.IsClass) != 0 && (other.flags & TypeFlags.IsStruct) != 0) { throw new SerializationException($"The type {this.TypeName} changed between versions from struct to class, which is not supported."); } else if ((this.flags & TypeFlags.IsStruct) != 0 && (other.flags & TypeFlags.IsClass) != 0) { throw new SerializationException($"The type {this.TypeName} changed between versions from class to struct, which is not supported."); } // required members in this schema must be present in the other schema var requiredAndMissing = this.Members.Where(mbr => mbr.IsRequired && !other.map.ContainsKey(mbr.Name)); if (requiredAndMissing.Count() > 0) { if (other.TypeName != this.TypeName) { if (TypeResolutionHelper.RemoveCoreAssemblyName(other.TypeName) == TypeResolutionHelper.RemoveCoreAssemblyName(this.TypeName)) { throw new SerializationException($"The type {other.TypeName} is not serialization format-compatible across framework versions as it is missing the following members required in the current version of {this.TypeName}: {string.Join(",", requiredAndMissing)}"); } else { throw new SerializationException($"The schema {other.Name} version {other.Version} (implemented by {other.TypeName}) is missing the following members required in the current version of {this.TypeName}: {string.Join(",", requiredAndMissing)}"); } } else { throw new SerializationException($"The type {this.TypeName} appears to have changed in a way that makes it incompatible with previous versions. The following members required by the new version are missing: {string.Join(",", requiredAndMissing)}"); } } // all members in the other schema need to be present in this schema requiredAndMissing = other.Members.Where(o => !this.map.ContainsKey(o.Name)); if (requiredAndMissing.Count() > 0) { if (other.TypeName != this.TypeName) { if (TypeResolutionHelper.RemoveCoreAssemblyName(other.TypeName) == TypeResolutionHelper.RemoveCoreAssemblyName(this.TypeName)) { throw new SerializationException($"The type {other.TypeName} is not serialization format-compatible across framework versions as it contains the following members which are not present in the current version of {this.TypeName}: {string.Join(",", requiredAndMissing)}"); } else { throw new SerializationException($"The schema {other.Name} version {other.Version} (implemented by {other.TypeName}) contains the following members which are not present in the current version of {this.TypeName}: {string.Join(",", requiredAndMissing)}"); } } else { throw new SerializationException($"The type {this.TypeName} appears to have changed in a way that makes it incompatible with previous versions. The following members required by the old version are missing in the new version: {string.Join(",", requiredAndMissing)}"); } } }
/// <summary> /// Registers a generic serializer, that is, a serializer defined for a generic type. /// The generic serializer must implement <see cref="ISerializer{T}"/>. /// </summary> /// <param name="genericSerializer">The type of generic serializer to register.</param> public void RegisterGenericSerializer(Type genericSerializer) { // var interf = genericSerializer.GetInterface("ISerializer`1"); var interf = genericSerializer.GetInterface(typeof(ISerializer <>).FullName); var serializableType = interf.GetGenericArguments()[0]; serializableType = TypeResolutionHelper.GetVerifiedType(serializableType.Namespace + "." + serializableType.Name); // FullName doesn't work here this.templates[serializableType] = genericSerializer; }
/// <summary> /// Register known types for serialization. /// </summary> public static void RegisterKnownSerializationTypes() { KnownSerializers.Default.Register <Queue <TimeSpan> >("System.Collections.Generic.Queue`1[[System.TimeSpan, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Collections, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); KnownSerializers.Default.Register <Queue <int> >("System.Collections.Generic.Queue`1[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Collections, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); KnownSerializers.Default.Register <Dictionary <int, PipelineDiagnostics> >("System.Collections.Generic.Dictionary`2[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Microsoft.Psi.Diagnostics.PipelineDiagnostics, Microsoft.Psi, Version=0.7.57.2, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"); KnownSerializers.Default.Register <Dictionary <int, PipelineDiagnostics.PipelineElementDiagnostics> >("System.Collections.Generic.Dictionary`2[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Microsoft.Psi.Diagnostics.PipelineDiagnostics+PipelineElementDiagnostics, Microsoft.Psi, Version=0.7.57.2, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"); KnownSerializers.Default.Register <Dictionary <int, PipelineDiagnostics.ReceiverDiagnostics> >("System.Collections.Generic.Dictionary`2[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Microsoft.Psi.Diagnostics.PipelineDiagnostics+ReceiverDiagnostics, Microsoft.Psi, Version=0.7.57.2, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"); KnownSerializers.Default.Register <Dictionary <int, PipelineDiagnostics.EmitterDiagnostics> >("System.Collections.Generic.Dictionary`2[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Microsoft.Psi.Diagnostics.PipelineDiagnostics+EmitterDiagnostics, Microsoft.Psi, Version=0.7.57.2, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"); KnownSerializers.Default.Register(TypeResolutionHelper.GetVerifiedType("System.Collections.Generic.GenericEqualityComparer`1[System.Int32]"), "System.Collections.Generic.GenericEqualityComparer`1[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"); }
public void TypeNameTest() { // primitive type string typeName = TypeResolutionHelper.RemoveAssemblyName(typeof(int).AssemblyQualifiedName); Assert.AreEqual(typeof(int).FullName, typeName); Assert.AreEqual(typeof(int), Type.GetType(typeName)); typeName = TypeResolutionHelper.RemoveAssemblyName(typeof(int[]).AssemblyQualifiedName); Assert.AreEqual(typeof(int[]).FullName, typeName); Assert.AreEqual(typeof(int[]), Type.GetType(typeName)); typeName = TypeResolutionHelper.RemoveAssemblyName(typeof(int[, ]).AssemblyQualifiedName); Assert.AreEqual(typeof(int[, ]).FullName, typeName); Assert.AreEqual(typeof(int[, ]), Type.GetType(typeName)); typeName = TypeResolutionHelper.RemoveAssemblyName(typeof(string).AssemblyQualifiedName); Assert.AreEqual(typeof(string).FullName, typeName); Assert.AreEqual(typeof(string), Type.GetType(typeName)); typeName = TypeResolutionHelper.RemoveAssemblyName(typeof(object).AssemblyQualifiedName); Assert.AreEqual(typeof(object).FullName, typeName); Assert.AreEqual(typeof(object), Type.GetType(typeName)); typeName = TypeResolutionHelper.RemoveAssemblyName(typeof(byte *).AssemblyQualifiedName); Assert.AreEqual(typeof(byte *).FullName, typeName); Assert.AreEqual(typeof(byte *), Type.GetType(typeName)); typeName = TypeResolutionHelper.RemoveAssemblyName(typeof(List <>).AssemblyQualifiedName); Assert.AreEqual(typeof(List <>).FullName, typeName); Assert.AreEqual(typeof(List <>), Type.GetType(typeName)); // Note - Type.FullName does not remove the AQN from the inner type parameters of generic // types, so we won't test the result for equality with typeof(List<int>).FullName typeName = TypeResolutionHelper.RemoveAssemblyName(typeof(List <int>).AssemblyQualifiedName); Assert.AreEqual(typeof(List <int>), Type.GetType(typeName)); typeName = TypeResolutionHelper.RemoveAssemblyName(typeof(IEnumerable <int>).AssemblyQualifiedName); Assert.AreEqual(typeof(IEnumerable <int>), Type.GetType(typeName)); typeName = TypeResolutionHelper.RemoveAssemblyName(typeof(IDictionary <int, List <int> >).AssemblyQualifiedName); Assert.AreEqual(typeof(IDictionary <int, List <int> >), Type.GetType(typeName)); typeName = TypeResolutionHelper.RemoveAssemblyName(typeof(Func <int, double>).AssemblyQualifiedName); Assert.AreEqual(typeof(Func <int, double>), Type.GetType(typeName)); typeName = TypeResolutionHelper.RemoveAssemblyName(typeof(NestedClass <List <int> >).AssemblyQualifiedName); Assert.AreEqual(typeof(NestedClass <List <int> >), Type.GetType(typeName)); typeName = TypeResolutionHelper.RemoveAssemblyName("Namespace.TypeName, AssemblyName WithSpaces-v1.0.0.0, Version=1.0.0.0"); Assert.AreEqual("Namespace.TypeName", typeName); typeName = TypeResolutionHelper.RemoveAssemblyName("Namespace.TypeName`2[[Nested.TypeName1, AssemblyName WithSpaces-v1.0.0.0, Version=1.0.0.0], [Nested.TypeName2[], AssemblyName, Culture=neutral]], AssemblyName, PublicKeyToken=null"); Assert.AreEqual("Namespace.TypeName`2[[Nested.TypeName1], [Nested.TypeName2[]]]", typeName); }
/// <summary> /// Creates and registers a handler for the specified type according to the rules added so far. /// </summary> /// <typeparam name="T">The type being serialized.</typeparam> /// <returns>The newly created handler.</returns> private SerializationHandler AddHandler <T>() { SerializationHandler handler = null; var type = typeof(T); ISerializer <T> serializer = null; TypeSchema schema = null; if (!this.knownNames.TryGetValue(type, out string name)) { name = TypeSchema.GetContractName(type, this.runtimeVersion); } if (!this.schemas.TryGetValue(name, out schema)) { // try to match to an existing schema without assembly/version info string typeName = TypeResolutionHelper.RemoveAssemblyName(type.AssemblyQualifiedName); schema = this.schemas.Values.FirstOrDefault(s => TypeResolutionHelper.RemoveAssemblyName(s.TypeName) == typeName); } int id = schema?.Id ?? TypeSchema.GetId(name); serializer = this.CreateSerializer <T>(); handler = SerializationHandler.Create <T>(serializer, schema?.Name ?? name, id); // first register the handler int oldCount = this.handlers.Length; var newHandlers = new SerializationHandler[oldCount + 1]; Array.Copy(this.handlers, newHandlers, oldCount); newHandlers[oldCount] = handler; this.handlers = newHandlers; var newIndex = new Dictionary <SerializationHandler, int>(this.index); newIndex[handler] = oldCount; this.index = newIndex; var newHandlersByType = new Dictionary <Type, SerializationHandler>(this.handlersByType); newHandlersByType[type] = handler; this.handlersByType = newHandlersByType; var newHandlersById = new Dictionary <int, SerializationHandler>(this.handlersById); newHandlersById[handler.Id] = handler; this.handlersById = newHandlersById; // find the schema for this serializer (can be null for interfaces) if (serializer != null) { // initialize the serializer after the handler is registered, // to make sure all handlers are registered before initialization runs and // allow the serializer initialization code to find and cache the handlers for the types it needs schema = serializer.Initialize(this, schema); // let any subscribers know that we initialized a new serializer that publishes a schema if (schema != null) { // store the updated schema and override whatever is present already this.schemas[schema.Name] = schema; this.schemasById[schema.Id] = schema; this.SchemaAdded?.Invoke(this, schema); } } return(handler); }
/// <inheritdoc/> public Type BindToType(string assemblyName, string typeName) { return(TypeResolutionHelper.GetVerifiedType($"{typeName}, {assemblyName}")); }
/// <summary> /// Create instance of stream reader (assumes ctor taking name and path. /// </summary> /// <param name="storeName">Store name.</param> /// <param name="storePath">Store path.</param> /// <param name="streamReaderTypeName">Stream reader type name.</param> /// <returns>Stream reader instance.</returns> public static IStreamReader Create(string storeName, string storePath, string streamReaderTypeName) { return(Create(storeName, storePath, TypeResolutionHelper.GetVerifiedType(streamReaderTypeName))); }