/// <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); } int id = this.schemas.TryGetValue(name, out schema) ? schema.Id : TypeSchema.GetId(name); serializer = this.CreateSerializer <T>(); handler = SerializationHandler.Create <T>(serializer, 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); }
public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema) { var schemaMembers = new[] { new TypeMemberSchema("buffer", typeof(byte[]).AssemblyQualifiedName, true) }; var type = typeof(MemoryStream); var name = TypeSchema.GetContractName(type, serializers.RuntimeVersion); var schema = new TypeSchema(name, TypeSchema.GetId(name), type.AssemblyQualifiedName, TypeFlags.IsCollection, schemaMembers, SchemaVersion); return(targetSchema ?? schema); }
public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema) { this.elementHandler = serializers.GetHandler <T>(); // register element type var type = typeof(T[]); var name = TypeSchema.GetContractName(type, serializers.RuntimeVersion); var elementsMember = new TypeMemberSchema("Elements", typeof(T).AssemblyQualifiedName, true); var schema = new TypeSchema(name, TypeSchema.GetId(name), type.AssemblyQualifiedName, TypeFlags.IsCollection, new TypeMemberSchema[] { elementsMember }, Version); return(targetSchema ?? schema); }
/// <summary> /// Registers a given type with the specified contract name. /// Use this overload to deserialize data persisted before a type name change. /// </summary> /// <param name="type">The type to use when deserializing objects with the specified contract.</param> /// <param name="contractName">The name to remap. This can be a full type name or a contract name.</param> public void Register(Type type, string contractName) { contractName = contractName ?? TypeSchema.GetContractName(type, this.runtimeVersion); if (this.knownTypes.TryGetValue(contractName, out Type existingType) && existingType != type) { throw new SerializationException($"Cannot register type {type.AssemblyQualifiedName} under the contract name {contractName} because the type {existingType.AssemblyQualifiedName} is already registered under the same name."); } this.knownTypes[contractName] = type; this.knownNames[type] = contractName; }
/// <summary> /// Registers a given type with the specified contract name. /// Use this overload to deserialize data persisted before a type name change. /// </summary> /// <param name="type">The type to use when deserializing objects with the specified contract.</param> /// <param name="contractName">The name to remap. This can be a full type name or a contract name.</param> /// <param name="cloningFlags">Optional flags that control the cloning behavior for this type.</param> public void Register(Type type, string contractName, CloningFlags cloningFlags = CloningFlags.None) { contractName = contractName ?? TypeSchema.GetContractName(type, this.runtimeVersion); if (this.knownTypes.TryGetValue(contractName, out Type existingType) && existingType != type) { throw new SerializationException($"Cannot register type {type.AssemblyQualifiedName} under the contract name {contractName} because the type {existingType.AssemblyQualifiedName} is already registered under the same name."); } if (this.cloningFlags.TryGetValue(type, out var existingFlags) || this.handlersByType.ContainsKey(type)) { // cannot re-register once type flags has been registered or handler has been created if (existingFlags != cloningFlags) { throw new SerializationException($"Cannot register type {type.AssemblyQualifiedName} with cloning flags ({cloningFlags}) because a handler for it has already been created with flags ({existingFlags})."); } } this.knownTypes[contractName] = type; this.knownNames[type] = contractName; this.cloningFlags[type] = cloningFlags; }
/// <inheritdoc /> public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema) { // Add a comparerHandler of type IEqualityComparer. This will take care of serializing, // deserializing and cloning the Comparer member of Dictionary. Because the comparer field // is private, we will also need to generate a dynamic method so that we can set the // comparer field upon deserialization or cloning. This method should be invoked right after // clearing the target Dictionary, before adding the deserialized or cloned entries to it. this.comparerHandler = serializers.GetHandler <IEqualityComparer <TKey> >(); this.setComparerImpl = this.GenerateSetComparerMethod(); // Use an array serializer to serialize the dictionary elements as an array of key-value pairs this.entriesHandler = serializers.GetHandler <KeyValuePair <TKey, TValue>[]>(); var type = typeof(Dictionary <TKey, TValue>); var name = TypeSchema.GetContractName(type, serializers.RuntimeVersion); // Treat the Dictionary as a class with 2 members - a comparer and an array of key-value pairs var comparerMember = new TypeMemberSchema("Comparer", typeof(IEqualityComparer <TKey>).AssemblyQualifiedName, true); var entriesMember = new TypeMemberSchema("KeyValuePairs", typeof(KeyValuePair <TKey, TValue>[]).AssemblyQualifiedName, true); var schema = new TypeSchema(name, TypeSchema.GetId(name), type.AssemblyQualifiedName, TypeFlags.IsClass, new[] { comparerMember, entriesMember }, SchemaVersion); return(targetSchema ?? schema); }
/// <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 try { 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); } } catch (SerializationException) { // Even though we're going to rethrow this exception, some callers may wish to // attempt to recover from this error and just mark this one stream type as // unreadable. So we should remove the handler we just registered as it's not // yet properly initialized. oldCount = this.handlers.Length; newHandlers = new SerializationHandler[oldCount - 1]; Array.Copy(this.handlers, newHandlers, oldCount - 1); this.handlers = newHandlers; newIndex = new Dictionary <SerializationHandler, int>(this.index); newIndex.Remove(handler); this.index = newIndex; newHandlersByType = new Dictionary <Type, SerializationHandler>(this.handlersByType); newHandlersByType.Remove(type); this.handlersByType = newHandlersByType; newHandlersById = new Dictionary <int, SerializationHandler>(this.handlersById); newHandlersById.Remove(handler.Id); this.handlersById = newHandlersById; throw; } } return(handler); }