Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialising JSON using JsonSerializer.DeserializeAsync is not using my JsonConverter

A server is returning a JSON string value which is a URL query string:

{
    "parameters": "key1=value1&key2=value2"
}

I have a property set up to receive this, and convert it into a Dictionary as part of the deserialisation process:

Property with JsonConverter attribute:

[JsonConverter(typeof(QueryStringToDictionaryJsonConverter))]
public Dictionary<string, string> Parameters { get; set; }

Converter:

public class QueryStringToDictionaryJsonConverter : JsonConverter<Dictionary<string, string>> {

    public override Dictionary<string, string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {

        var queryString = reader.GetString();
        if (string.IsNullOrEmpty(queryString)) return null;

        return QueryHelpers.ParseQuery(queryString).ToDictionary(e => e.Key, e => string.Join(",", e.Value.ToArray()));

    }

    ...
}

This should work.

But it's not even getting to my converter.

From what I can tell, JsonSerializer.DeserializeAsync<T>(myJson) is seeing that the type of property is a Dictionary, and so it tries to parse the value as such on its own, and fails (the resulting exception is an 'invalid cast' as it tries to GetEnumerable() etc). A breakpoint in my converter never even gets hit.

I can get it to work by making the property an object and then casting to a Dictionary later where it's used, but that's an ugly solution.

Is there a way to force JsonSerializer.DeserializeAsync<T>(myJson) to just use my converter, without it trying to be smart on its own?

(I'm using Microsoft's System.Text.Json in .NET Core 3)

like image 637
Ross Avatar asked Oct 27 '22 07:10

Ross


1 Answers

OK, so this could be a bug in System.Text.Json.

Here's the workaround I'm currently using for anyone else needing a solution.

First, I set up two properties for deserialisation, using [JsonPropertyName] and [JsonIgnore]:

[JsonPropertyName("parameters"), JsonConverter(typeof(QueryStringToDictionaryJsonConverter))]
public object ParametersObject { get; set; }

[JsonIgnore]
public Dictionary<string, string> Parameters => ParametersObject as Dictionary<string, string>;

And then in the JsonConverter, I allow object as the type:

public override bool CanConvert(Type typeToConvert) {
    if (typeToConvert == typeof(object)) return true;
    return base.CanConvert(typeToConvert);
}

Consumers of my deserialised class just use the Parameters property, which will continue to work just fine if and when this bug is fixed and I change the class back to how I'd like it.

like image 120
Ross Avatar answered Nov 09 '22 05:11

Ross