示例#1
0
        /// <summary>
        /// Builds a serialization function for the Confluent wire format.
        /// </summary>
        /// <param name="id">
        /// A schema ID to include in each serialized payload.
        /// </param>
        /// <param name="schema">
        /// The schema to build the Avro serializer from.
        /// </param>
        /// <returns>
        /// A function configured to serialize <typeparamref name="T" /> to an array of bytes.
        /// </returns>
        protected virtual Func <T, byte[]> Build(int id, Abstract.Schema schema)
        {
            var header = new byte[5];

            Array.Copy(BitConverter.GetBytes(id), 0, header, 1, 4);

            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(header, 1, 4);
            }

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

            var streamConstructor = typeof(MemoryStream)
                                    .GetConstructor(Type.EmptyTypes);

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

            var dispose = typeof(IDisposable)
                          .GetMethod(nameof(IDisposable.Dispose), Type.EmptyTypes);

            var toArray = typeof(MemoryStream)
                          .GetMethod(nameof(MemoryStream.ToArray), Type.EmptyTypes);

            var write = typeof(Stream)
                        .GetMethod(nameof(Stream.Write), new[] { typeof(byte[]), typeof(int), typeof(int) });

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

            var writer = Expression.Block(
                new[] { stream },
                Expression.Assign(stream, Expression.New(streamConstructor)),
                Expression.TryFinally(
                    Expression.Block(
                        Expression.Call(
                            stream,
                            write,
                            Expression.Constant(header),
                            Expression.Constant(0),
                            Expression.Constant(header.Length)),
                        Expression.Invoke(
                            inner,
                            value,
                            Expression.New(writerConstructor, stream))),
                    Expression.Call(stream, dispose)),
                Expression.Call(stream, toArray));

            return(Expression.Lambda <Func <T, byte[]> >(writer, value).Compile());
        }
示例#2
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));
        }