/// <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="schema"> /// The schema to build the Avro deserializer from. /// </param> protected virtual IDeserializer <T> Build <T>(int id, string schema) { var deserialize = DeserializerBuilder.BuildDelegate <T>(SchemaReader.Read(schema)); return(new DelegateDeserializer <T>(stream => { 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); })); }
/// <summary> /// Deserialize a message. (See <see cref="IAsyncDeserializer{T}.DeserializeAsync(ReadOnlyMemory{byte}, bool, SerializationContext)" />.) /// </summary> public virtual async Task <T> DeserializeAsync(ReadOnlyMemory <byte> data, bool isNull, SerializationContext context) { using (var stream = new MemoryStream(data.ToArray(), 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 @delegate = await(_cache.GetOrAdd(BitConverter.ToInt32(bytes, 0), async id => { var json = await RegistryClient.GetSchemaAsync(id).ConfigureAwait(false); var schema = SchemaReader.Read(json); return(DeserializerBuilder.BuildDelegate <T>(schema)); })).ConfigureAwait(false); return(@delegate(stream)); } }
/// <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); } })); }