コード例 #1
0
        /// <summary>
        /// Creates a deserializer.
        /// </summary>
        /// <param name="registryClient">
        /// The client to use for Schema Registry operations. (The client will not be disposed.)
        /// </param>
        /// <param name="deserializerBuilder">
        /// The deserializer builder used to generate deserialization functions for C# types. If
        /// none is provided, the default deserializer builder will be used.
        /// </param>
        /// <param name="schemaReader">
        /// The JSON schema reader used to convert schemas received from the registry into abstract
        /// representations. If none is provided, the default schema reader will be used.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// The behavior of the deserializer on tombstone records.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when the registry client is null.
        /// </exception>
        public AsyncSchemaRegistryDeserializer(
            ISchemaRegistryClient registryClient,
            IBinaryDeserializerBuilder deserializerBuilder = null,
            IJsonSchemaReader schemaReader      = null,
            TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None
            )
        {
            if (registryClient == null)
            {
                throw new ArgumentNullException(nameof(registryClient));
            }

            if (tombstoneBehavior != TombstoneBehavior.None && default(T) != null)
            {
                throw new UnsupportedTypeException(typeof(T), $"{typeof(T)} cannot represent tombstone values.");
            }

            DeserializerBuilder = deserializerBuilder ?? new BinaryDeserializerBuilder();
            RegistryClient      = registryClient;
            SchemaReader        = schemaReader ?? new JsonSchemaReader();
            TombstoneBehavior   = tombstoneBehavior;

            _cache = new Dictionary <int, Task <Func <Stream, T> > >();
            _disposeRegistryClient = false;
        }
コード例 #2
0
 /// <summary>
 /// Sets an Avro deserializer for values.
 /// </summary>
 /// <typeparam name="TKey">
 /// The type of key to be deserialized.
 /// </typeparam>
 /// <typeparam name="TValue">
 /// The type of value to be deserialized.
 /// </typeparam>
 /// <param name="consumerBuilder">
 /// A <see cref="ConsumerBuilder{TKey, TValue}" /> instance to be configured.
 /// </param>
 /// <param name="deserializerBuilder">
 /// A deserializer builder.
 /// </param>
 /// <param name="subject">
 /// The subject of the schema that should be used to deserialize values. The latest version
 /// of the subject will be resolved.
 /// </param>
 /// <param name="tombstoneBehavior">
 /// How the deserializer should handle tombstone records.
 /// </param>
 /// <returns>
 /// <paramref name="consumerBuilder" /> with an Avro deserializer configured for
 /// <typeparamref name="TValue" />.
 /// </returns>
 public static async Task <ConsumerBuilder <TKey, TValue> > SetAvroValueDeserializer <TKey, TValue>(
     this ConsumerBuilder <TKey, TValue> consumerBuilder,
     ISchemaRegistryDeserializerBuilder deserializerBuilder,
     string subject,
     TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None)
 => consumerBuilder.SetValueDeserializer(
     await deserializerBuilder.Build <TValue>(subject, tombstoneBehavior).ConfigureAwait(false));
コード例 #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="AsyncSchemaRegistrySerializer{T}" />
        /// class with a Schema Registry configuration.
        /// </summary>
        /// <param name="registryConfiguration">
        /// A Schema Registry configuration. Using the <see cref="SchemaRegistryConfig" /> class is
        /// highly recommended.
        /// </param>
        /// <param name="registerAutomatically">
        /// Whether the serializer should automatically register schemas that match the type being
        /// serialized.
        /// </param>
        /// <param name="schemaBuilder">
        /// A schema builder instance that should be used to create schemas for .NET <see cref="Type" />s
        /// when registering automatically. If none is provided, the default <see cref="SchemaBuilder" />
        /// will be used.
        /// </param>
        /// <param name="schemaReader">
        /// A schema reader instance that should be used to convert schemas received from the
        /// Registry into abstract representations. If none is provided, the default
        /// <see cref="JsonSchemaReader" /> will be used.
        /// </param>
        /// <param name="schemaWriter">
        /// A schema writer instance that should be used to convert abstract schema representations
        /// when registering automatically. If none is provided, the default <see cref="JsonSchemaWriter" />
        /// will be used.
        /// </param>
        /// <param name="serializerBuilder">
        /// A serializer builder instance that should be used to generate serialization functions
        /// for .NET <see cref="Type" />s. If none is provided, the default <see cref="BinarySerializerBuilder" />
        /// will be used.
        /// </param>
        /// <param name="subjectNameBuilder">
        /// A function that determines a subject name given the topic name and a component type
        /// (key or value). If none is provided, the default <c>{topic name}-{component}</c> naming
        /// convention will be used.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// How the serializer should handle tombstone records.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when <paramref name="registryConfiguration" /> is <c>null</c>.
        /// </exception>
        /// <exception cref="UnsupportedTypeException">
        /// Thrown when <paramref name="tombstoneBehavior" /> is incompatible with
        /// <typeparamref name="T" />.
        /// </exception>
        public AsyncSchemaRegistrySerializer(
            IEnumerable <KeyValuePair <string, string> > registryConfiguration,
            AutomaticRegistrationBehavior registerAutomatically = AutomaticRegistrationBehavior.Never,
            Abstract.ISchemaBuilder schemaBuilder                  = null,
            IJsonSchemaReader schemaReader                         = null,
            IJsonSchemaWriter schemaWriter                         = null,
            IBinarySerializerBuilder serializerBuilder             = null,
            Func <SerializationContext, string> subjectNameBuilder = null,
            TombstoneBehavior tombstoneBehavior                    = TombstoneBehavior.None)
        {
            if (registryConfiguration == null)
            {
                throw new ArgumentNullException(nameof(registryConfiguration));
            }

            if (tombstoneBehavior != TombstoneBehavior.None && default(T) != null)
            {
                throw new UnsupportedTypeException(typeof(T), $"{typeof(T)} cannot represent tombstone values.");
            }

            RegisterAutomatically = registerAutomatically;
            RegistryClient        = new CachedSchemaRegistryClient(registryConfiguration);
            SchemaBuilder         = schemaBuilder ?? new Abstract.SchemaBuilder();
            SchemaReader          = schemaReader ?? new JsonSchemaReader();
            SchemaWriter          = schemaWriter ?? new JsonSchemaWriter();
            SerializerBuilder     = serializerBuilder ?? new BinarySerializerBuilder();
            SubjectNameBuilder    = subjectNameBuilder ??
                                    (c => $"{c.Topic}-{(c.Component == MessageComponentType.Key ? "key" : "value")}");
            TombstoneBehavior = tombstoneBehavior;

            cache = new Dictionary <string, Task <Func <T, byte[]> > >();
            disposeRegistryClient = true;
        }
コード例 #4
0
        /// <summary>
        /// Creates a serializer.
        /// </summary>
        /// <param name="registryClient">
        /// The client to use for Schema Registry operations. (The client will not be disposed.)
        /// </param>
        /// <param name="registerAutomatically">
        /// When to automatically register schemas that match the type being serialized.
        /// </param>
        /// <param name="schemaBuilder">
        /// The schema builder to use to create a schema for a C# type when registering automatically.
        /// If none is provided, the default schema builder will be used.
        /// </param>
        /// <param name="schemaReader">
        /// The JSON schema reader to use to convert schemas received from the registry into abstract
        /// representations. If none is provided, the default schema reader will be used.
        /// </param>
        /// <param name="schemaWriter">
        /// The JSON schema writer to use to convert abstract schema representations when registering
        /// automatically. If none is provided, the default schema writer will be used.
        /// </param>
        /// <param name="serializerBuilder">
        /// The deserializer builder to use to build serialization functions for C# types. If none
        /// is provided, the default serializer builder will be used.
        /// </param>
        /// <param name="subjectNameBuilder">
        /// A function that determines the subject name given the topic name and a component type
        /// (key or value). If none is provided, the default "{topic name}-{component}" naming
        /// convention will be used.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// The behavior of the serializer on tombstone records.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when the registry client is null.
        /// </exception>
        public AsyncSchemaRegistrySerializer(
            ISchemaRegistryClient registryClient,
            AutomaticRegistrationBehavior registerAutomatically = AutomaticRegistrationBehavior.Never,
            Abstract.ISchemaBuilder schemaBuilder                  = null,
            IJsonSchemaReader schemaReader                         = null,
            IJsonSchemaWriter schemaWriter                         = null,
            IBinarySerializerBuilder serializerBuilder             = null,
            Func <SerializationContext, string> subjectNameBuilder = null,
            TombstoneBehavior tombstoneBehavior                    = TombstoneBehavior.None
            )
        {
            if (registryClient == null)
            {
                throw new ArgumentNullException(nameof(registryClient));
            }

            if (tombstoneBehavior != TombstoneBehavior.None && default(T) != null)
            {
                throw new UnsupportedTypeException(typeof(T), $"{typeof(T)} cannot represent tombstone values.");
            }

            RegisterAutomatically = registerAutomatically;
            SchemaBuilder         = schemaBuilder ?? new Abstract.SchemaBuilder();
            SchemaReader          = schemaReader ?? new JsonSchemaReader();
            SchemaWriter          = schemaWriter ?? new JsonSchemaWriter();
            SerializerBuilder     = serializerBuilder ?? new BinarySerializerBuilder();
            SubjectNameBuilder    = subjectNameBuilder ??
                                    (c => $"{c.Topic}-{(c.Component == MessageComponentType.Key ? "key" : "value")}");
            TombstoneBehavior = tombstoneBehavior;

            _cache    = new Dictionary <string, Task <Func <T, byte[]> > >();
            _register = (subject, json) => registryClient.RegisterSchemaAsync(subject, json);
            _resolve  = subject => registryClient.GetLatestSchemaAsync(subject);
        }
コード例 #5
0
 /// <summary>
 /// Builds a deserializer for a specific schema.
 /// </summary>
 /// <param name="id">
 /// The ID of the schema that should be used to deserialize data.
 /// </param>
 /// <param name="tombstoneBehavior">
 /// The behavior of the deserializer on tombstone records.
 /// </param>
 /// <exception cref="AggregateException">
 /// Thrown when the type is incompatible with the retrieved schema.
 /// </exception>
 public virtual async Task <IDeserializer <T> > Build <T>(
     int id,
     TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None
     )
 {
     return(Build <T>(id, await RegistryClient.GetSchemaAsync(id).ConfigureAwait(false), tombstoneBehavior));
 }
コード例 #6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="DelegateSerializer{T}" /> class.
        /// </summary>
        /// <param name="delegate">
        /// A serialization function for <typeparamref name="T" />.
        /// </param>
        /// <param name="schemaId">
        /// The ID of the schema that <paramref name="delegate" /> was built against.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// How the serializer should handle tombstone records.
        /// </param>
        public DelegateSerializer(Implementation @delegate, int schemaId, TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None)
        {
            SchemaId          = schemaId;
            TombstoneBehavior = tombstoneBehavior;

            this.@delegate = @delegate ?? throw new ArgumentNullException(nameof(@delegate));
        }
コード例 #7
0
        /// <summary>
        /// Creates a deserializer.
        /// </summary>
        /// <param name="registryConfiguration">
        /// Schema Registry configuration. Using the <see cref="SchemaRegistryConfig" /> class is
        /// highly recommended.
        /// </param>
        /// <param name="deserializerBuilder">
        /// The deserializer builder to use to to generate deserialization functions for C# types.
        /// If none is provided, the default deserializer builder will be used.
        /// </param>
        /// <param name="schemaReader">
        /// The JSON schema reader to use to convert schemas received from the registry into abstract
        /// representations. If none is provided, the default schema reader will be used.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// The behavior of the deserializer on tombstone records.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when the registry configuration is null.
        /// </exception>
        public AsyncSchemaRegistryDeserializer(
            IEnumerable <KeyValuePair <string, string> > registryConfiguration,
            IBinaryDeserializerBuilder deserializerBuilder = null,
            IJsonSchemaReader schemaReader      = null,
            TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None
            )
        {
            if (registryConfiguration == null)
            {
                throw new ArgumentNullException(nameof(registryConfiguration));
            }

            if (tombstoneBehavior != TombstoneBehavior.None && default(T) != null)
            {
                throw new UnsupportedTypeException(typeof(T), $"{typeof(T)} cannot represent tombstone values.");
            }

            DeserializerBuilder = deserializerBuilder ?? new BinaryDeserializerBuilder();
            RegistryClient      = new CachedSchemaRegistryClient(registryConfiguration);
            SchemaReader        = schemaReader ?? new JsonSchemaReader();
            TombstoneBehavior   = tombstoneBehavior;

            _cache = new Dictionary <int, Task <Func <Stream, T> > >();
            _disposeRegistryClient = true;
        }
コード例 #8
0
        /// <summary>
        /// Builds a serializer for a specific schema.
        /// </summary>
        /// <param name="id">
        /// The ID of the schema that should be used to serialize data.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// The behavior of the serializer on tombstone records.
        /// </param>
        /// <exception cref="AggregateException">
        /// Thrown when the type is incompatible with the retrieved schema.
        /// </exception>
        public async Task <ISerializer <T> > Build <T>(
            int id,
            TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None
            )
        {
            var schema = await RegistryClient.GetSchemaAsync(id).ConfigureAwait(false);

            return(Build <T>(id, schema, tombstoneBehavior));
        }
コード例 #9
0
 /// <summary>
 /// Set the message value deserializer.
 /// </summary>
 /// <param name="consumerBuilder">
 /// The <see cref="ConsumerBuilder{TKey, TValue}" /> instance to be configured.
 /// </param>
 /// <param name="registryConfiguration">
 /// Schema Registry configuration. Using the <see cref="SchemaRegistryConfig" /> class is
 /// highly recommended.
 /// </param>
 /// <param name="tombstoneBehavior">
 /// The behavior of the deserializer on tombstone records.
 /// </param>
 public static ConsumerBuilder <TKey, TValue> SetAvroValueDeserializer <TKey, TValue>(
     this ConsumerBuilder <TKey, TValue> consumerBuilder,
     IEnumerable <KeyValuePair <string, string> > registryConfiguration,
     TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None
     ) => consumerBuilder.SetValueDeserializer(
     new AsyncSchemaRegistryDeserializer <TValue>(
         registryConfiguration,
         tombstoneBehavior: tombstoneBehavior
         ).AsSyncOverAsync());
コード例 #10
0
        /// <summary>
        /// Builds a deserializer for a specific schema.
        /// </summary>
        /// <param name="subject">
        /// The subject of the schema that should be used to deserialize data. The latest version
        /// of the subject will be resolved.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// The behavior of the deserializer on tombstone records.
        /// </param>
        /// <exception cref="AggregateException">
        /// Thrown when the type is incompatible with the retrieved schema.
        /// </exception>
        public virtual async Task <IDeserializer <T> > Build <T>(
            string subject,
            TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None
            )
        {
            var schema = await RegistryClient.GetLatestSchemaAsync(subject).ConfigureAwait(false);

            return(Build <T>(schema.Id, schema.SchemaString, tombstoneBehavior));
        }
コード例 #11
0
 /// <summary>
 /// Set the message value deserializer.
 /// </summary>
 /// <param name="consumerBuilder">
 /// The <see cref="ConsumerBuilder{TKey, TValue}" /> instance to be configured.
 /// </param>
 /// <param name="registryClient">
 /// The client to use for Schema Registry operations. The client should only be disposed
 /// after the consumer; the deserializer will use it to request schemas as messages are
 /// being consumed.
 /// </param>
 /// <param name="tombstoneBehavior">
 /// The behavior of the deserializer on tombstone records.
 /// </param>
 public static ConsumerBuilder <TKey, TValue> SetAvroValueDeserializer <TKey, TValue>(
     this ConsumerBuilder <TKey, TValue> consumerBuilder,
     ISchemaRegistryClient registryClient,
     TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None
     ) => consumerBuilder.SetValueDeserializer(
     new AsyncSchemaRegistryDeserializer <TValue>(
         registryClient,
         tombstoneBehavior: tombstoneBehavior
         ).AsSyncOverAsync());
コード例 #12
0
        /// <summary>
        /// Builds a serializer for the Confluent wire format.
        /// </summary>
        /// <param name="id">
        /// A schema ID to include in each serialized payload.
        /// </param>
        /// <param name="json">
        /// The schema to build the Avro serializer from.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// The behavior of the serializer on tombstone records.
        /// </param>
        protected virtual ISerializer <T> Build <T>(
            int id,
            string json,
            TombstoneBehavior tombstoneBehavior
            )
        {
            var schema = SchemaReader.Read(json);

            if (tombstoneBehavior != TombstoneBehavior.None)
            {
                if (default(T) != null)
                {
                    throw new UnsupportedTypeException(typeof(T), $"{typeof(T)} cannot represent tombstone values.");
                }

                var hasNull = schema is Abstract.NullSchema ||
                              (schema is Abstract.UnionSchema union && union.Schemas.Any(s => s is Abstract.NullSchema));

                if (tombstoneBehavior == TombstoneBehavior.Strict && hasNull)
                {
                    throw new UnsupportedSchemaException(schema, "Tombstone serialization is not supported for schemas that can represent null values.");
                }
            }

            var bytes = BitConverter.GetBytes(id);

            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(bytes);
            }

            var serialize = SerializerBuilder.BuildDelegate <T>(schema);

            return(new DelegateSerializer <T>((data, context) =>
            {
                if (data == null && tombstoneBehavior != TombstoneBehavior.None)
                {
                    if (context.Component == MessageComponentType.Value || tombstoneBehavior != TombstoneBehavior.Strict)
                    {
                        return null;
                    }
                }

                var stream = new MemoryStream();

                using (stream)
                {
                    stream.WriteByte(0x00);
                    stream.Write(bytes, 0, bytes.Length);

                    serialize(data, stream);
                }

                return stream.ToArray();
            }));
        }
コード例 #13
0
        /// <summary>
        /// Creates a serializer.
        /// </summary>
        /// <param name="registryConfiguration">
        /// Schema Registry configuration. Using the <see cref="SchemaRegistryConfig" /> class is
        /// highly recommended.
        /// </param>
        /// <param name="registerAutomatically">
        /// When to automatically register schemas that match the type being serialized.
        /// </param>
        /// <param name="schemaBuilder">
        /// The schema builder to use to create a schema for a C# type when registering automatically.
        /// If none is provided, the default schema builder will be used.
        /// </param>
        /// <param name="schemaReader">
        /// The JSON schema reader to use to convert schemas received from the registry into abstract
        /// representations. If none is provided, the default schema reader will be used.
        /// </param>
        /// <param name="schemaWriter">
        /// The JSON schema writer to use to convert abstract schema representations when registering
        /// automatically. If none is provided, the default schema writer will be used.
        /// </param>
        /// <param name="serializerBuilder">
        /// The deserializer builder to use to build serialization functions for C# types. If none
        /// is provided, the default serializer builder will be used.
        /// </param>
        /// <param name="subjectNameBuilder">
        /// A function that determines the subject name given the topic name and a component type
        /// (key or value). If none is provided, the default "{topic name}-{component}" naming
        /// convention will be used.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// The behavior of the serializer on tombstone records.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when the registry configuration is null.
        /// </exception>
        public AsyncSchemaRegistrySerializer(
            IEnumerable <KeyValuePair <string, string> > registryConfiguration,
            AutomaticRegistrationBehavior registerAutomatically = AutomaticRegistrationBehavior.Never,
            Abstract.ISchemaBuilder schemaBuilder                  = null,
            IJsonSchemaReader schemaReader                         = null,
            IJsonSchemaWriter schemaWriter                         = null,
            IBinarySerializerBuilder serializerBuilder             = null,
            Func <SerializationContext, string> subjectNameBuilder = null,
            TombstoneBehavior tombstoneBehavior                    = TombstoneBehavior.None
            )
        {
            if (registryConfiguration == null)
            {
                throw new ArgumentNullException(nameof(registryConfiguration));
            }

            if (tombstoneBehavior != TombstoneBehavior.None && default(T) != null)
            {
                throw new UnsupportedTypeException(typeof(T), $"{typeof(T)} cannot represent tombstone values.");
            }

            RegisterAutomatically = registerAutomatically;
            SchemaBuilder         = schemaBuilder ?? new Abstract.SchemaBuilder();
            SchemaReader          = schemaReader ?? new JsonSchemaReader();
            SchemaWriter          = schemaWriter ?? new JsonSchemaWriter();
            SerializerBuilder     = serializerBuilder ?? new BinarySerializerBuilder();
            SubjectNameBuilder    = subjectNameBuilder ??
                                    (c => $"{c.Topic}-{(c.Component == MessageComponentType.Key ? "key" : "value")}");
            TombstoneBehavior = tombstoneBehavior;

            _cache = new Dictionary <string, Task <Func <T, byte[]> > >();

            _register = async(subject, json) =>
            {
                using (var registry = new CachedSchemaRegistryClient(registryConfiguration))
                {
                    return(await registry.RegisterSchemaAsync(subject, new Schema(json, SchemaType.Avro)).ConfigureAwait(false));
                }
            };

            _resolve = async subject =>
            {
                using (var registry = new CachedSchemaRegistryClient(registryConfiguration))
                {
                    var schema = await registry.GetLatestSchemaAsync(subject).ConfigureAwait(false);

                    if (schema.SchemaType != SchemaType.Avro)
                    {
                        throw new UnsupportedSchemaException(null, $"The latest schema with subject {subject} is not an Avro schema.");
                    }

                    return(schema);
                }
            };
        }
コード例 #14
0
        /// <summary>
        /// Builds a serializer for a specific schema.
        /// </summary>
        /// <param name="subject">
        /// The subject of the schema that should be used to serialize data.
        /// </param>
        /// <param name="version">
        /// The version of the subject to be resolved.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// The behavior of the serializer on tombstone records.
        /// </param>
        /// <exception cref="AggregateException">
        /// Thrown when the type is incompatible with the retrieved schema.
        /// </exception>
        public virtual async Task <ISerializer <T> > Build <T>(
            string subject,
            int version,
            TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None
            )
        {
            var schema = await RegistryClient.GetSchemaAsync(subject, version).ConfigureAwait(false);

            var id = await RegistryClient.GetSchemaIdAsync(subject, schema).ConfigureAwait(false);

            return(Build <T>(id, schema, tombstoneBehavior));
        }
コード例 #15
0
        /// <summary>
        /// Sets an Avro deserializer for values.
        /// </summary>
        /// <typeparam name="TKey">
        /// The type of key to be deserialized.
        /// </typeparam>
        /// <typeparam name="TValue">
        /// The type of value to be deserialized.
        /// </typeparam>
        /// <param name="consumerBuilder">
        /// A <see cref="ConsumerBuilder{TKey, TValue}" /> instance to be configured.
        /// </param>
        /// <param name="registryClient">
        /// A Schema Registry client to use to resolve the schema. (The client will not be
        /// disposed.)
        /// </param>
        /// <param name="id">
        /// The ID of the schema that should be used to deserialize values.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// How the deserializer should handle tombstone records.
        /// </param>
        /// <returns>
        /// <paramref name="consumerBuilder" /> with an Avro deserializer configured for
        /// <typeparamref name="TValue" />.
        /// </returns>
        public static async Task <ConsumerBuilder <TKey, TValue> > SetAvroValueDeserializer <TKey, TValue>(
            this ConsumerBuilder <TKey, TValue> consumerBuilder,
            ISchemaRegistryClient registryClient,
            int id,
            TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None)
        {
            using var deserializerBuilder = new SchemaRegistryDeserializerBuilder(registryClient);

            return(await consumerBuilder
                   .SetAvroValueDeserializer(deserializerBuilder, id, tombstoneBehavior)
                   .ConfigureAwait(false));
        }
コード例 #16
0
        /// <summary>
        /// Sets an Avro deserializer for values.
        /// </summary>
        /// <typeparam name="TKey">
        /// The type of key to be deserialized.
        /// </typeparam>
        /// <typeparam name="TValue">
        /// The type of value to be deserialized.
        /// </typeparam>
        /// <param name="consumerBuilder">
        /// A <see cref="ConsumerBuilder{TKey, TValue}" /> instance to be configured.
        /// </param>
        /// <param name="registryConfiguration">
        /// A Schema Registry configuration. Using the <see cref="SchemaRegistryConfig" /> class is
        /// highly recommended.
        /// </param>
        /// <param name="subject">
        /// The subject of the schema that should be used to deserialize values. The latest version
        /// of the subject will be resolved.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// How the deserializer should handle tombstone records.
        /// </param>
        /// <returns>
        /// <paramref name="consumerBuilder" /> with an Avro deserializer configured for
        /// <typeparamref name="TValue" />.
        /// </returns>
        public static async Task <ConsumerBuilder <TKey, TValue> > SetAvroValueDeserializer <TKey, TValue>(
            this ConsumerBuilder <TKey, TValue> consumerBuilder,
            IEnumerable <KeyValuePair <string, string> > registryConfiguration,
            string subject,
            TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None)
        {
            using var deserializerBuilder = new SchemaRegistryDeserializerBuilder(registryConfiguration);

            return(await consumerBuilder
                   .SetAvroValueDeserializer(deserializerBuilder, subject, tombstoneBehavior)
                   .ConfigureAwait(false));
        }
コード例 #17
0
        /// <exception cref="UnsupportedTypeException">
        /// Thrown when <typeparamref name="T" /> is incompatible with the retrieved schema.
        /// </exception>
        /// <inheritdoc />
        public virtual async Task <IDeserializer <T> > Build <T>(
            string subject,
            TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None)
        {
            var schema = await RegistryClient.GetLatestSchemaAsync(subject).ConfigureAwait(false);

            if (schema.SchemaType != SchemaType.Avro)
            {
                throw new UnsupportedSchemaException(null, $"The latest schema with subject {subject} is not an Avro schema.");
            }

            return(Build <T>(schema.Id, schema.SchemaString, tombstoneBehavior));
        }
コード例 #18
0
        /// <exception cref="UnsupportedTypeException">
        /// Thrown when <typeparamref name="T" /> is incompatible with the retrieved schema.
        /// </exception>
        /// <inheritdoc />
        public async Task <ISerializer <T> > Build <T>(
            int id,
            TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None)
        {
            var schema = await RegistryClient.GetSchemaAsync(id).ConfigureAwait(false);

            if (schema.SchemaType != SchemaType.Avro)
            {
                throw new UnsupportedSchemaException(null, $"The schema with ID {id} is not an Avro schema.");
            }

            return(Build <T>(id, schema.SchemaString, tombstoneBehavior));
        }
コード例 #19
0
        /// <summary>
        /// Builds a serializer for the Confluent wire format.
        /// </summary>
        /// <typeparam name="T">
        /// The type to be deserialized.
        /// </typeparam>
        /// <param name="id">
        /// A schema ID to include in each serialized payload.
        /// </param>
        /// <param name="json">
        /// The schema to build the Avro serializer from.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// The behavior of the serializer on tombstone records.
        /// </param>
        /// <returns>
        /// A <see cref="ISerializer{T}" /> based on <paramref name="json" />.
        /// </returns>
        protected virtual ISerializer <T> Build <T>(
            int id,
            string json,
            TombstoneBehavior tombstoneBehavior)
        {
            var schema = SchemaReader.Read(json);

            if (tombstoneBehavior != TombstoneBehavior.None)
            {
                if (default(T) != null)
                {
                    throw new UnsupportedTypeException(typeof(T), $"{typeof(T)} cannot represent tombstone values.");
                }

                var hasNull = schema is Abstract.NullSchema ||
                              (schema is Abstract.UnionSchema union && union.Schemas.Any(s => s is Abstract.NullSchema));

                if (tombstoneBehavior == TombstoneBehavior.Strict && hasNull)
                {
                    throw new UnsupportedSchemaException(schema, "Tombstone serialization is not supported for schemas that can represent null values.");
                }
            }

            var inner  = SerializerBuilder.BuildDelegateExpression <T>(schema);
            var stream = Expression.Parameter(typeof(Stream));
            var value  = inner.Parameters[0];

            var writerConstructor = inner.Parameters[1].Type
                                    .GetConstructor(new[] { stream.Type });

            if (schema is Abstract.BytesSchema)
            {
                inner = new WireFormatBytesSerializerRewriter(stream)
                        .VisitAndConvert(inner, GetType().Name);
            }

            return(new DelegateSerializer <T>(
                       Expression
                       .Lambda <DelegateSerializer <T> .Implementation>(
                           Expression.Invoke(
                               inner,
                               value,
                               Expression.New(writerConstructor, stream)),
                           new[] { value, stream })
                       .Compile(),
                       id,
                       tombstoneBehavior));
        }
コード例 #20
0
        /// <exception cref="UnsupportedTypeException">
        /// Thrown when <typeparamref name="T" /> is incompatible with the retrieved schema.
        /// </exception>
        /// <inheritdoc />
        public virtual async Task <IDeserializer <T> > Build <T>(
            string subject,
            int version,
            TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None)
        {
            var schema = await RegistryClient.GetRegisteredSchemaAsync(subject, version).ConfigureAwait(false);

            if (schema.SchemaType != SchemaType.Avro)
            {
                throw new UnsupportedSchemaException(null, $"The schema with subject {subject} and version {version} is not an Avro schema.");
            }

            var id = await RegistryClient.GetSchemaIdAsync(subject, schema).ConfigureAwait(false);

            return(Build <T>(id, schema.SchemaString, tombstoneBehavior));
        }
コード例 #21
0
        /// <exception cref="UnsupportedTypeException">
        /// Thrown when <typeparamref name="T" /> is incompatible with the retrieved schema.
        /// </exception>
        /// <inheritdoc />
        public async Task <ISerializer <T> > Build <T>(
            string subject,
            AutomaticRegistrationBehavior registerAutomatically = AutomaticRegistrationBehavior.Never,
            TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None)
        {
            switch (registerAutomatically)
            {
            case AutomaticRegistrationBehavior.Always:
                var json = SchemaWriter.Write(SchemaBuilder.BuildSchema <T>());
                var id   = await RegistryClient.RegisterSchemaAsync(subject, new Schema(json, SchemaType.Avro)).ConfigureAwait(false);

                return(Build <T>(id, json, tombstoneBehavior));

            case AutomaticRegistrationBehavior.Never:
                var existing = await RegistryClient.GetLatestSchemaAsync(subject).ConfigureAwait(false);

                return(Build <T>(existing.Id, existing.SchemaString, tombstoneBehavior));

            default:
                throw new ArgumentOutOfRangeException(nameof(registerAutomatically));
            }
        }
コード例 #22
0
        /// <summary>
        /// Builds a deserializer for the Confluent wire format.
        /// </summary>
        /// <param name="id">
        /// A schema ID that all payloads must be serialized with. If a received schema ID does not
        /// match this ID, <see cref="InvalidDataException" /> will be thrown.
        /// </param>
        /// <param name="json">
        /// The schema to build the Avro deserializer from.
        /// </param>
        /// <param name="tombstoneBehavior">
        /// The behavior of the deserializer on tombstone records.
        /// </param>
        protected virtual IDeserializer <T> Build <T>(
            int id,
            string json,
            TombstoneBehavior tombstoneBehavior
            )
        {
            var schema = SchemaReader.Read(json);

            if (tombstoneBehavior != TombstoneBehavior.None)
            {
                if (default(T) != null)
                {
                    throw new UnsupportedTypeException(typeof(T), $"{typeof(T)} cannot represent tombstone values.");
                }

                var hasNull = schema is NullSchema ||
                              (schema is UnionSchema union && union.Schemas.Any(s => s is NullSchema));

                if (tombstoneBehavior == TombstoneBehavior.Strict && hasNull)
                {
                    throw new UnsupportedSchemaException(schema, "Tombstone deserialization is not supported for schemas that can represent null values.");
                }
            }

            var deserialize = DeserializerBuilder.BuildDelegate <T>(schema);

            return(new DelegateDeserializer <T>((data, isNull, context) =>
            {
                if (isNull && tombstoneBehavior != TombstoneBehavior.None)
                {
                    if (context.Component == MessageComponentType.Value || tombstoneBehavior != TombstoneBehavior.Strict)
                    {
                        return default;
                    }
                }

                using (var stream = new MemoryStream(data, false))
                {
                    var bytes = new byte[4];

                    if (stream.ReadByte() != 0x00 || stream.Read(bytes, 0, bytes.Length) != bytes.Length)
                    {
                        throw new InvalidDataException("Data does not conform to the Confluent wire format.");
                    }

                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(bytes);
                    }

                    var received = BitConverter.ToInt32(bytes, 0);

                    if (received != id)
                    {
                        throw new InvalidDataException($"The received schema ({received}) does not match the specified schema ({id}).");
                    }

                    return deserialize(stream);
                }
            }));
        }