nats-io / nats.net

Full Async C# / .NET client for NATS
https://nats-io.github.io/nats.net
Apache License 2.0
239 stars 49 forks source link

Serialization exception when putting to object store #404

Closed ronnieoverby closed 7 months ago

ronnieoverby commented 7 months ago

Observed behavior

When trying to put bytes into an object store, I got an InvalidOperationException throw by System.Text.Json.

I observed this problem when using the reflection based json serializer : NatsJsonSerializerRegistry.Default

Expected behavior

I just want my bytes to be stored.

Server and client version

Server: 2.10.7 Client: https://www.nuget.org/packages/NATS.Net/2.1.0

Host environment

Windows 11 / .NET 8

Steps to reproduce

async Task Main()
{
    await using var nats = new NatsConnection(NatsOpts.Default with
    {
        SerializerRegistry = NatsJsonSerializerRegistry.Default // removing this avoids the exception
    });

    await nats.ConnectAsync();

    var js = new NatsJSContext(nats);
    var os = new NatsObjContext(js);

    var store = await os.CreateObjectStoreAsync(new NatsObjConfig("bucket1"));
    try
    {           
        await store.PutAsync("obj1", Guid.NewGuid().ToByteArray());
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());

        /*
System.InvalidOperationException: The type 'System.Span`1[System.Byte]' of property 'Span' on type 'NATS.Client.Core.NatsMemoryOwner`1[System.Byte]' is invalid for serialization or deserialization because it is a pointer type, is a ref struct, or contains generic parameters that have not been replaced by specific types.
   at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_CannotSerializeInvalidType(Type typeToConvert, Type declaringType, MemberInfo memberInfo)
   at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.CreatePropertyInfo(JsonTypeInfo typeInfo, Type typeToConvert, MemberInfo memberInfo, JsonSerializerOptions options, Boolean shouldCheckForRequiredKeyword, Boolean hasJsonIncludeAttribute)
   at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.AddMember(JsonTypeInfo typeInfo, Type typeToConvert, MemberInfo memberInfo, Boolean shouldCheckForRequiredKeyword, Boolean hasJsonIncludeAttribute, PropertyHierarchyResolutionState& state)
   at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.AddMembersDeclaredBySuperType(JsonTypeInfo typeInfo, Type currentType, Boolean constructorHasSetsRequiredMembersAttribute, PropertyHierarchyResolutionState& state)
   at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.PopulateProperties(JsonTypeInfo typeInfo)
   at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.CreateTypeInfoCore(Type type, JsonConverter converter, JsonSerializerOptions options)
   at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.CreateJsonTypeInfo(Type type, JsonSerializerOptions options)
   at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.GetTypeInfo(Type type, JsonSerializerOptions options)
   at System.Text.Json.JsonSerializerOptions.CachingContext.CreateCacheEntry(Type type, CachingContext context)
--- End of stack trace from previous location ---
   at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type type, Boolean ensureConfigured, Nullable`1 ensureNotNull, Boolean resolveIfMutable, Boolean fallBackToNearestAncestorType)
   at System.Text.Json.JsonSerializerOptions.GetTypeInfoForRootType(Type type, Boolean fallBackToNearestAncestorType)
   at System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions options, Type inputType)
   at System.Text.Json.JsonSerializer.GetTypeInfo[T](JsonSerializerOptions options)
   at System.Text.Json.JsonSerializer.Serialize[TValue](Utf8JsonWriter writer, TValue value, JsonSerializerOptions options)
   at NATS.Client.Serializers.Json.NatsJsonSerializer`1.Serialize(IBufferWriter`1 bufferWriter, T value)
   at NATS.Client.Core.Commands.CommandWriter.PublishAsync[T](String subject, T value, NatsHeaders headers, String replyTo, INatsSerialize`1 serializer, CancellationToken cancellationToken)
   at NATS.Client.Core.NatsConnection.PublishAsync[T](String subject, T data, NatsHeaders headers, String replyTo, INatsSerialize`1 serializer, NatsPubOpts opts, CancellationToken cancellationToken)
   at NATS.Client.Core.NatsConnection.RequestSubAsync[TRequest,TReply](String subject, TRequest data, NatsHeaders headers, INatsSerialize`1 requestSerializer, INatsDeserialize`1 replySerializer, NatsPubOpts requestOpts, NatsSubOpts replyOpts, CancellationToken cancellationToken)
   at NATS.Client.JetStream.NatsJSContext.PublishAsync[T](String subject, T data, INatsSerialize`1 serializer, NatsJSPubOpts opts, NatsHeaders headers, CancellationToken cancellationToken)
   at NATS.Client.ObjectStore.NatsObjStore.PutAsync(ObjectMetadata meta, Stream stream, Boolean leaveOpen, CancellationToken cancellationToken)
   at NATS.Client.ObjectStore.NatsObjStore.PutAsync(ObjectMetadata meta, Stream stream, Boolean leaveOpen, CancellationToken cancellationToken)
   at UserQuery.Main() in C:\Users\ronnie.overby\AppData\Local\Temp\LINQPad8\_utfnwvhq\pvrfnp\LINQPadQuery:line 16
        */
    }
}
mtmk commented 7 months ago

Thanks for the report 💯 It's a bug and fix is on it's way!