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

System.Text.Json: Calling Write method of default dictionary converter from a custom dictionary converter fails with NRE #106697

Closed
elgonzo opened this issue Aug 20, 2024 · 2 comments
Labels
area-System.Text.Json needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration
Milestone

Comments

@elgonzo
Copy link

elgonzo commented Aug 20, 2024

Description

STJ version: 9.0.0-preview.7.24405.7

Given is an example custom Dictionary<string, int> dictionary converter (stripped down to the bits essential to the issue at hand) which invokes the default dictionary converter (obtained from JsonSerializerOptions.Default) in its Write method.

class CustomIntDictionaryConverter : JsonConverter<Dictionary<string, int>>
{
    public override Dictionary<string, int>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => throw new NotImplementedException();

    public override void Write(Utf8JsonWriter writer, Dictionary<string, int> value, JsonSerializerOptions options)
    {
        var c = (JsonConverter<Dictionary<string, int>>) JsonSerializerOptions.Default.GetConverter(typeof(Dictionary<string, int>));
        c.Write(writer, value, options);
    }
}

Serializing a Dictionary<string, int> dictionary using this converter fails with a NRE in the default converter's internal OnTryWrite method because it looks like state.Current.JsonTypeInfo.ElementTypeInfo is null when trying to get jsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo:

state.Current.JsonPropertyInfo = jsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo;

Reproduction Steps

var dictInt = new Dictionary<string, int>
{
    ["Foo"] = 0,
    ["Bar"] = 1,
    ["Baz"] = 2
};

var opts = new JsonSerializerOptions();
opts.Converters.Add(new CustomIntDictionaryConverter());

string json = JsonSerializer.Serialize(dictInt, opts);


class CustomIntDictionaryConverter : JsonConverter<Dictionary<string, int>>
{
    public override Dictionary<string, int>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => throw new NotImplementedException();

    public override void Write(Utf8JsonWriter writer, Dictionary<string, int> value, JsonSerializerOptions options)
    {
        var c = (JsonConverter<Dictionary<string, int>>) JsonSerializerOptions.Default.GetConverter(typeof(Dictionary<string, int>));
        c.Write(writer, value, options);
    }
}

Expected behavior

The dictionary should be deserialized normally without exceptions.

Actual behavior

System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Text.Json.Serialization.JsonDictionaryConverter`3.OnTryWrite(Utf8JsonWriter writer, TDictionary dictionary, 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.JsonResumableConverter`1.Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
   at CustomIntDictionaryConverter.Write(Utf8JsonWriter writer, Dictionary`2 value, JsonSerializerOptions options) in X:\ConsoleApp1\ConsoleApp1\Program.cs:line 153
   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.Serialization.Metadata.JsonTypeInfo`1.Serialize(Utf8JsonWriter writer, T& rootValue, Object rootValueBoxed)
   at System.Text.Json.JsonSerializer.WriteString[TValue](TValue& value, JsonTypeInfo`1 jsonTypeInfo)
   at System.Text.Json.JsonSerializer.Serialize[TValue](TValue value, JsonSerializerOptions options)
   at Program.Main() in X:\ConsoleApp1\ConsoleApp1\Program.cs:line 42

Regression?

Not sure, but i don't think so.
I tested also with 8.0.4, 8.0.0, as well as 7.0.4, and these three also throw an NRE.

Known Workarounds

So far I got no idea for a workaround except (re)implementing the dictionary serialization logic in the custom converter. Which can become quite burdensome if the custom converter is using a generic type parameter for the dictionary value type (with the help of a converter factory).

Configuration

.NET version: 8.0
STJ version: 9.0.0-preview.7.24405.7
OS: Windows 11 x64

Other information

No response

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Aug 20, 2024
Copy link
Contributor

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

@jeffhandley jeffhandley added needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration and removed untriaged New issue has not been triaged by the area owner labels Aug 23, 2024
@jeffhandley jeffhandley added this to the Future milestone Aug 23, 2024
@eiriktsarpalis
Copy link
Member

Duplicate of #50205. Built-in converters not being usable outside of their configuration context is a known defect of the STJ serialization infrastructure.

@github-actions github-actions bot locked and limited conversation to collaborators Sep 28, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Text.Json needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration
Projects
None yet
Development

No branches or pull requests

3 participants