Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON source gen does not work correctly with nullable structs with custom converter #70475

Closed
krwq opened this issue Jun 9, 2022 · 5 comments
Labels
area-System.Text.Json bug source-generator Indicates an issue with a source generator feature
Milestone

Comments

@krwq
Copy link
Member

krwq commented Jun 9, 2022

Repro:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace Repro
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // prints: {"Bar":"B"}
            Console.WriteLine(JsonSerializer.Serialize<FooStruct>(new FooStruct { Bar = Foo.B }));

            // error, see below for more details
            Console.WriteLine(JsonSerializer.Serialize<FooStruct>(new FooStruct { Bar = Foo.B }, EnumContext.Default.FooStruct));
        }
    }

    [JsonSerializable(typeof(FooStruct))]
    internal partial class EnumContext : JsonSerializerContext
    {

    }

    internal struct FooStruct
    {
        [JsonConverter(typeof(JsonStringEnumConverter))]
        public Foo? Bar { get; set; }
    }

    internal enum Foo
    {
        A,
        B
    }
}

Exception message:

Unhandled exception. System.ArgumentException: GenericArguments[0], 'System.Nullable`1[Repro.Foo]', on 'System.Text.Json.Serialization.Converters.EnumConverter`1[T]' violates the constraint of type 'T'.
 ---> System.TypeLoadException: GenericArguments[0], 'System.Nullable`1[Repro.Foo]', on 'System.Text.Json.Serialization.Converters.EnumConverter`1[T]' violates the constraint of type parameter 'T'.
   at System.RuntimeTypeHandle.Instantiate(QCallTypeHandle handle, IntPtr* pInst, Int32 numGenericArgs, ObjectHandleOnStack type)
   at System.RuntimeTypeHandle.Instantiate(RuntimeType inst)
   at System.RuntimeType.MakeGenericType(Type[] instantiation)
   --- End of inner exception stack trace ---
   at System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e)
   at System.RuntimeType.MakeGenericType(Type[] instantiation)
   at System.Text.Json.Serialization.Converters.EnumConverterFactory.Create(Type enumType, EnumConverterOptions converterOptions, JsonNamingPolicy namingPolicy, JsonSerializerOptions serializerOptions)
   at Repro.EnumContext.GetConverterFromFactory(Type type, JsonConverterFactory factory) in C:\Users\kwicher\source\repos\ConsoleApp89\ConsoleApp89\System.Text.Json.SourceGeneration\System.Text.Json.SourceGeneration.JsonSourceGenerator\EnumContext.g.cs:line 69
   at Repro.EnumContext.GetConverterFromFactory[T](JsonConverterFactory factory) in C:\Users\kwicher\source\repos\ConsoleApp89\ConsoleApp89\System.Text.Json.SourceGeneration\System.Text.Json.SourceGeneration.JsonSourceGenerator\EnumContext.g.cs:line 64
   at Repro.EnumContext.FooStructPropInit(JsonSerializerContext context) in C:\Users\kwicher\source\repos\ConsoleApp89\ConsoleApp89\System.Text.Json.SourceGeneration\System.Text.Json.SourceGeneration.JsonSourceGenerator\EnumContext.FooStruct.g.cs:line 50
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo.InitializePropCache()
   at System.Text.Json.Serialization.Converters.JsonMetadataServicesConverter`1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.JsonSerializer.WriteUsingSerializer[TValue](Utf8JsonWriter writer, TValue& value, JsonTypeInfo jsonTypeInfo)
   at System.Text.Json.JsonSerializer.WriteUsingGeneratedSerializer[TValue](Utf8JsonWriter writer, TValue& value, JsonTypeInfo jsonTypeInfo)
   at System.Text.Json.JsonSerializer.WriteStringUsingGeneratedSerializer[TValue](TValue& value, JsonTypeInfo jsonTypeInfo)
   at System.Text.Json.JsonSerializer.Serialize[TValue](TValue value, JsonTypeInfo`1 jsonTypeInfo)
   at Repro.Program.Main(String[] args) in C:\Users\kwicher\source\repos\ConsoleApp89\ConsoleApp89\Program.cs:line 10
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Jun 9, 2022
@ghost
Copy link

ghost commented Jun 9, 2022

Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis
See info in area-owners.md if you want to be subscribed.

Issue Details

Repro:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace Repro
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // prints: {"Bar":"B"}
            Console.WriteLine(JsonSerializer.Serialize<FooStruct>(new FooStruct { Bar = Foo.B }));

            // error, see below for more details
            Console.WriteLine(JsonSerializer.Serialize<FooStruct>(new FooStruct { Bar = Foo.B }, EnumContext.Default.FooStruct));
        }
    }

    [JsonSerializable(typeof(FooStruct))]
    internal partial class EnumContext : JsonSerializerContext
    {

    }

    internal struct FooStruct
    {
        [JsonConverter(typeof(JsonStringEnumConverter))]
        public Foo? Bar { get; set; }
    }

    internal enum Foo
    {
        A,
        B
    }
}

Exception message:

Unhandled exception. System.ArgumentException: GenericArguments[0], 'System.Nullable`1[Repro.Foo]', on 'System.Text.Json.Serialization.Converters.EnumConverter`1[T]' violates the constraint of type 'T'.
 ---> System.TypeLoadException: GenericArguments[0], 'System.Nullable`1[Repro.Foo]', on 'System.Text.Json.Serialization.Converters.EnumConverter`1[T]' violates the constraint of type parameter 'T'.
   at System.RuntimeTypeHandle.Instantiate(QCallTypeHandle handle, IntPtr* pInst, Int32 numGenericArgs, ObjectHandleOnStack type)
   at System.RuntimeTypeHandle.Instantiate(RuntimeType inst)
   at System.RuntimeType.MakeGenericType(Type[] instantiation)
   --- End of inner exception stack trace ---
   at System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e)
   at System.RuntimeType.MakeGenericType(Type[] instantiation)
   at System.Text.Json.Serialization.Converters.EnumConverterFactory.Create(Type enumType, EnumConverterOptions converterOptions, JsonNamingPolicy namingPolicy, JsonSerializerOptions serializerOptions)
   at Repro.EnumContext.GetConverterFromFactory(Type type, JsonConverterFactory factory) in C:\Users\kwicher\source\repos\ConsoleApp89\ConsoleApp89\System.Text.Json.SourceGeneration\System.Text.Json.SourceGeneration.JsonSourceGenerator\EnumContext.g.cs:line 69
   at Repro.EnumContext.GetConverterFromFactory[T](JsonConverterFactory factory) in C:\Users\kwicher\source\repos\ConsoleApp89\ConsoleApp89\System.Text.Json.SourceGeneration\System.Text.Json.SourceGeneration.JsonSourceGenerator\EnumContext.g.cs:line 64
   at Repro.EnumContext.FooStructPropInit(JsonSerializerContext context) in C:\Users\kwicher\source\repos\ConsoleApp89\ConsoleApp89\System.Text.Json.SourceGeneration\System.Text.Json.SourceGeneration.JsonSourceGenerator\EnumContext.FooStruct.g.cs:line 50
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo.InitializePropCache()
   at System.Text.Json.Serialization.Converters.JsonMetadataServicesConverter`1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.JsonSerializer.WriteUsingSerializer[TValue](Utf8JsonWriter writer, TValue& value, JsonTypeInfo jsonTypeInfo)
   at System.Text.Json.JsonSerializer.WriteUsingGeneratedSerializer[TValue](Utf8JsonWriter writer, TValue& value, JsonTypeInfo jsonTypeInfo)
   at System.Text.Json.JsonSerializer.WriteStringUsingGeneratedSerializer[TValue](TValue& value, JsonTypeInfo jsonTypeInfo)
   at System.Text.Json.JsonSerializer.Serialize[TValue](TValue value, JsonTypeInfo`1 jsonTypeInfo)
   at Repro.Program.Main(String[] args) in C:\Users\kwicher\source\repos\ConsoleApp89\ConsoleApp89\Program.cs:line 10
Author: krwq
Assignees: -
Labels:

area-System.Text.Json

Milestone: -

@krwq krwq added this to the 7.0.0 milestone Jun 9, 2022
@krwq krwq added bug and removed untriaged New issue has not been triaged by the area owner labels Jun 9, 2022
@krwq
Copy link
Member Author

krwq commented Jun 9, 2022

Another similar case:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace Repro
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(JsonSerializer.Serialize<FooStruct>(new FooStruct { Bar = Foo.B }, EnumContext.Default.FooStruct));
        }
    }

    [JsonSerializable(typeof(FooStruct))]
    internal partial class EnumContext : JsonSerializerContext
    {

    }

    internal struct FooStruct
    {
        [JsonConverter(typeof(FooConverter))]
        public Foo? Bar { get; set; }
    }

    internal class FooConverter : JsonConverter<Foo>
    {
        public override Foo Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            return (Foo)reader.GetInt32();
        }

        public override void Write(Utf8JsonWriter writer, Foo value, JsonSerializerOptions options)
        {
            writer.WriteNumberValue((int)value);
        }
    }

    internal enum Foo
    {
        A,
        B
    }
}

fails to compile:

Error	CS0029	Cannot implicitly convert type 'Repro.FooConverter' to 'System.Text.Json.Serialization.JsonConverter<Repro.Foo?>'	ConsoleApp89	C:\Users\kwicher\source\repos\ConsoleApp89\ConsoleApp89\System.Text.Json.SourceGeneration\System.Text.Json.SourceGeneration.JsonSourceGenerator\EnumContext.FooStruct.g.cs	57	Active

@krwq
Copy link
Member Author

krwq commented Jul 20, 2022

this seems like an existing bug without external reports (found only while doing unrelated code changes which touched this) and also a bit too risky to do at this point in release so moving to Future

@eiriktsarpalis eiriktsarpalis added the source-generator Indicates an issue with a source generator feature label Sep 2, 2022
@smokedlinq
Copy link

smokedlinq commented Dec 5, 2022

@krwq ran across this today. Any suggested workarounds while we wait for a fix? Other than not using the source generator...

@eiriktsarpalis
Copy link
Member

Fixed in #84208

@ghost ghost locked as resolved and limited conversation to collaborators Jul 8, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Text.Json bug source-generator Indicates an issue with a source generator feature
Projects
None yet
Development

No branches or pull requests

3 participants