aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs
blob: 8053461f08c4b02ae0bdf8453f81da7fc6130fdd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#nullable enable

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace MediaBrowser.Common.Json.Converters
{
    /// <summary>
    /// Converter for Dictionaries without string key.
    /// TODO This can be removed when System.Text.Json supports Dictionaries with non-string keys.
    /// </summary>
    /// <typeparam name="TKey">Type of key.</typeparam>
    /// <typeparam name="TValue">Type of value.</typeparam>
    internal sealed class JsonNonStringKeyDictionaryConverter<TKey, TValue> : JsonConverter<IDictionary<TKey, TValue>>
    {
        /// <summary>
        /// Read JSON.
        /// </summary>
        /// <param name="reader">The Utf8JsonReader.</param>
        /// <param name="typeToConvert">The type to convert.</param>
        /// <param name="options">The json serializer options.</param>
        /// <returns>Typed dictionary.</returns>
        /// <exception cref="NotSupportedException">Dictionary key type not supported.</exception>
        public override IDictionary<TKey, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            var convertedType = typeof(Dictionary<,>).MakeGenericType(typeof(string), typeToConvert.GenericTypeArguments[1]);
            var value = JsonSerializer.Deserialize(ref reader, convertedType, options);
            var instance = (Dictionary<TKey, TValue>)Activator.CreateInstance(
                typeToConvert,
                BindingFlags.Instance | BindingFlags.Public,
                null,
                null,
                CultureInfo.CurrentCulture);
            var enumerator = (IEnumerator)convertedType.GetMethod("GetEnumerator")!.Invoke(value, null);
            var parse = typeof(TKey).GetMethod(
                "Parse",
                0,
                BindingFlags.Public | BindingFlags.Static,
                null,
                CallingConventions.Any,
                new[] { typeof(string) },
                null);
            if (parse == null)
            {
                throw new NotSupportedException($"{typeof(TKey)} as TKey in IDictionary<TKey, TValue> is not supported.");
            }

            while (enumerator.MoveNext())
            {
                var element = (KeyValuePair<string?, TValue>)enumerator.Current;
                instance.Add((TKey)parse.Invoke(null, new[] { (object?)element.Key }), element.Value);
            }

            return instance;
        }

        /// <summary>
        /// Write dictionary as Json.
        /// </summary>
        /// <param name="writer">The Utf8JsonWriter.</param>
        /// <param name="value">The dictionary value.</param>
        /// <param name="options">The Json serializer options.</param>
        public override void Write(Utf8JsonWriter writer, IDictionary<TKey, TValue> value, JsonSerializerOptions options)
        {
            var convertedDictionary = new Dictionary<string?, TValue>(value.Count);
            foreach (var (k, v) in value)
            {
                if (k != null)
                {
                    convertedDictionary[k.ToString()] = v;
                }
            }

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