JsonNonStringKeyDictionaryConverter.cs 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. #nullable enable
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Globalization;
  6. using System.Reflection;
  7. using System.Text.Json;
  8. using System.Text.Json.Serialization;
  9. namespace MediaBrowser.Common.Json.Converters
  10. {
  11. /// <summary>
  12. /// Converter for Dictionaries without string key.
  13. /// TODO This can be removed when System.Text.Json supports Dictionaries with non-string keys.
  14. /// </summary>
  15. /// <typeparam name="TKey">Type of key.</typeparam>
  16. /// <typeparam name="TValue">Type of value.</typeparam>
  17. internal sealed class JsonNonStringKeyDictionaryConverter<TKey, TValue> : JsonConverter<IDictionary<TKey, TValue>>
  18. {
  19. /// <summary>
  20. /// Read JSON.
  21. /// </summary>
  22. /// <param name="reader">The Utf8JsonReader.</param>
  23. /// <param name="typeToConvert">The type to convert.</param>
  24. /// <param name="options">The json serializer options.</param>
  25. /// <returns>Typed dictionary.</returns>
  26. /// <exception cref="NotSupportedException">Not supported.</exception>
  27. public override IDictionary<TKey, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
  28. {
  29. var convertedType = typeof(Dictionary<,>).MakeGenericType(typeof(string), typeToConvert.GenericTypeArguments[1]);
  30. var value = JsonSerializer.Deserialize(ref reader, convertedType, options);
  31. var instance = (Dictionary<TKey, TValue>)Activator.CreateInstance(
  32. typeToConvert,
  33. BindingFlags.Instance | BindingFlags.Public,
  34. null,
  35. null,
  36. CultureInfo.CurrentCulture);
  37. var enumerator = (IEnumerator)convertedType.GetMethod("GetEnumerator")!.Invoke(value, null);
  38. var parse = typeof(TKey).GetMethod(
  39. "Parse",
  40. 0,
  41. BindingFlags.Public | BindingFlags.Static,
  42. null,
  43. CallingConventions.Any,
  44. new[] { typeof(string) },
  45. null);
  46. if (parse == null)
  47. {
  48. throw new NotSupportedException($"{typeof(TKey)} as TKey in IDictionary<TKey, TValue> is not supported.");
  49. }
  50. while (enumerator.MoveNext())
  51. {
  52. var element = (KeyValuePair<string?, TValue>)enumerator.Current;
  53. instance.Add((TKey)parse.Invoke(null, new[] { (object?)element.Key }), element.Value);
  54. }
  55. return instance;
  56. }
  57. /// <summary>
  58. /// Write dictionary as Json.
  59. /// </summary>
  60. /// <param name="writer">The Utf8JsonWriter.</param>
  61. /// <param name="value">The dictionary value.</param>
  62. /// <param name="options">The Json serializer options.</param>
  63. public override void Write(Utf8JsonWriter writer, IDictionary<TKey, TValue> value, JsonSerializerOptions options)
  64. {
  65. var convertedDictionary = new Dictionary<string?, TValue>(value.Count);
  66. foreach (var (k, v) in value)
  67. {
  68. if (k != null)
  69. {
  70. convertedDictionary[k.ToString()] = v;
  71. }
  72. }
  73. JsonSerializer.Serialize(writer, convertedDictionary, options);
  74. }
  75. }
  76. }