Runtime: [System.Text.Json] Support Dictionary<int, T>

Created on 19 Aug 2019  路  10Comments  路  Source: dotnet/runtime

Hello. When I try to serialize Dictionary with integer key, it throw System.NotSupportedException.

I think it makes sense to support Json serialization which Dictionary has ToString-able key. for example, when we run ToString for int or boolean, it return "123" or "true". I think that key is ToString-able key.

Verison

System.Text.Json Nuget Version : 4.6.0-preview8.19405.3

Code

var dictionary = new Dictionary<int, string>
{
  [5] = "five"
};
JsonSerializer.Serialize(dictionary);

Expected

"{"5": "five"}"

But what happen

Error System.NotSupportedException Thrown


Actually, there is compatibility problem when I change Newtonsoft.Json to System.Text.Json. They return string as I expected. I think System.Text.Json don't have to be compatible but... you know.

area-System.Text.Json json-functionality-doc

Most helpful comment

@namse yes, Please take a look at https://github.com/dotnet/corefx/issues/36639#issue-429928740 for examples (of inheriting from JsonConverter & using it)

All 10 comments

Related: https://github.com/dotnet/corefx/issues/40120

Both "not supported exception" errors are limitations within the built-in serializer and is by design (at least for what is shipping in 3.0).

When serializing, only Dictionary is supported today (i.e. TKeys that are string). Your dictionary is of which isn't supported.

https://github.com/dotnet/corefx/issues/40120#issuecomment-519648806

As @Gnbrkm41 mentioned, this is a known limitation and is a duplicate of https://github.com/dotnet/corefx/issues/40120

@namse, I have moved your comment to the existing issue. Please try the converter approach and let us know if your issue persists.

@ahsonkhan You mean, the converter approach is using JsonConverter to solve this problem, right?

@namse yes, Please take a look at https://github.com/dotnet/corefx/issues/36639#issue-429928740 for examples (of inheriting from JsonConverter & using it)

@ahsonkhan You mean, the converter approach is using JsonConverter to solve this problem, right?

Yes.

The workaround for this is to use a custom converter that implements JsonConverter<T> where T is Dictionary<int, T>.
See https://github.com/dotnet/corefx/issues/36639#issue-429928740 for some examples (and also look at the built-in converters in source and ones in tests):
https://github.com/dotnet/corefx/tree/master/src/System.Text.Json/src/System/Text/Json/Serialization/Converters
https://github.com/dotnet/corefx/blob/master/src/System.Text.Json/tests/Serialization/CustomConverterTests.Dictionary.cs

I made JsonConverter which support Non-string Key Dictionary. Only serialization.

It can handle bool, number, and Enum.

public class NonStringKeyDictionaryJsonConverterFactory : System.Text.Json.Serialization.JsonConverterFactory
{
    private static Type[] ConvertableTypes = new Type[]
    {
        typeof(bool),
        typeof(byte),
        typeof(char),
        typeof(decimal),
        typeof(double),
        typeof(float),
        typeof(int),
        typeof(long),
        typeof(sbyte),
        typeof(short),
        typeof(uint),
        typeof(ulong),
        typeof(ushort),
    };

    public static bool CanConvertImpl(Type typeToConvert)
    {
        if (typeToConvert.IsGenericType
            && typeToConvert.GetGenericTypeDefinition() == typeof(Dictionary<,>))
        {
            var keyType = typeToConvert.GenericTypeArguments[0];
            return keyType.IsEnum || ConvertableTypes.Any(convertableType => keyType == convertableType);
        }

        var baseType = typeToConvert.BaseType;
        if (!(baseType is null))
        {
            return CanConvertImpl(baseType);
        }

        return false;
    }

    public override bool CanConvert(Type typeToConvert)
    {
        return CanConvertImpl(typeToConvert);
    }

    public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
    {
        var converterType = typeof(NonStringKeyDictionaryJsonConverter<,>)
            .MakeGenericType(typeToConvert.GenericTypeArguments[0], typeToConvert.GenericTypeArguments[1]);

        var converter = (JsonConverter)Activator.CreateInstance(
            converterType,
            BindingFlags.Instance | BindingFlags.Public,
            binder: null,
            new object[] {
                //_converterOptions, _namingPolicy
            },
            culture: null);

        return converter;
    }
}

public class NonStringKeyDictionaryJsonConverter<TKey, TValue>
    : System.Text.Json.Serialization.JsonConverter<Dictionary<TKey, TValue>>
{
    public override bool CanConvert(Type typeToConvert)
    {
        return NonStringKeyDictionaryJsonConverterFactory.CanConvertImpl(typeToConvert);
    }

    public override Dictionary<TKey, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }

    public override void Write(Utf8JsonWriter writer, Dictionary<TKey, TValue> enumKeyDictionary, JsonSerializerOptions options)
    {
        var stringKeyDictionary = new Dictionary<string, object>(enumKeyDictionary.Count);

        foreach (var (key, value) in enumKeyDictionary)
        {
            var stringKey = key.ToString();
            stringKeyDictionary[stringKey] = value;
        }

        JsonSerializer.Serialize(writer, stringKeyDictionary, options);
    }
}

It works, is it right pattern?

It works, is it right pattern?

cc @steveharter, @layomia - can you help verify the converter sample shared by @namse

Why is this not supported ? The key of the dictionary is a string. And its value an object. JsonSerializer.Deserialize<Dictionary<string, Dictionary<VoiceAction, string>>>(VoiceCommands);

Can you share an isolated repro along with the error/behavior you are seeing in your use case? Also, are you running on .NET Core 3.1?

I managed to make it work. I had to include the DictionaryTKeyEnumTValueConverter described here: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to#support-dictionary-with-non-string-key

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jzabroski picture jzabroski  路  3Comments

yahorsi picture yahorsi  路  3Comments

nalywa picture nalywa  路  3Comments

chunseoklee picture chunseoklee  路  3Comments

EgorBo picture EgorBo  路  3Comments