Demonstrate converters for Dictionary<TKey, TValue> where TKey is not String (dotnet...
authorSteve Harter <steveharter@users.noreply.github.com>
Thu, 3 Oct 2019 22:49:04 +0000 (17:49 -0500)
committerGitHub <noreply@github.com>
Thu, 3 Oct 2019 22:49:04 +0000 (17:49 -0500)
Commit migrated from https://github.com/dotnet/corefx/commit/eff9fbf446468ca411bf5e0475916f316eea7223

src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.DictionaryConverterForIDictionary.cs [moved from src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.Dictionary.cs with 56% similarity]
src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.DictionaryGuidConverter.cs [new file with mode: 0644]
src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.DictionaryInt32StringConverter.cs [new file with mode: 0644]
src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.DictionaryInt32StringKeyValueConverter.cs [new file with mode: 0644]
src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.DictionaryKeyValueConverter.cs [new file with mode: 0644]
src/libraries/System.Text.Json/tests/Serialization/OptionsTests.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests.csproj

@@ -9,17 +9,25 @@ namespace System.Text.Json.Serialization.Tests
 {
     public static partial class CustomConverterTests
     {
-        // Demonstrates custom Dictionary<string, long>; Adds offset to each integer or long to verify converter ran.
-        internal class DictionaryConverter : JsonConverter<Dictionary<string, long>>
+        /// <summary>
+        /// Demonstrates custom <see cref="Dictionary{string, long}">.
+        /// Adds offset to each value to verify the converter ran.
+        /// </summary>
+        private class DictionaryConverterForIDictionary : JsonConverter<IDictionary<string, long>>
         {
             private long _offset;
 
-            public DictionaryConverter(long offset)
+            public DictionaryConverterForIDictionary(long offset)
             {
                 _offset = offset;
             }
 
-            public override Dictionary<string, long> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+            public override bool CanConvert(Type typeToConvert)
+            {
+                return typeToConvert == typeof(Dictionary<string, long>);
+            }
+
+            public override IDictionary<string, long> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
             {
                 if (reader.TokenType != JsonTokenType.StartObject)
                 {
@@ -56,7 +64,7 @@ namespace System.Text.Json.Serialization.Tests
                 throw new JsonException();
             }
 
-            public override void Write(Utf8JsonWriter writer, Dictionary<string, long> value, JsonSerializerOptions options)
+            public override void Write(Utf8JsonWriter writer, IDictionary<string, long> value, JsonSerializerOptions options)
             {
                 writer.WriteStartObject();
 
@@ -70,30 +78,12 @@ namespace System.Text.Json.Serialization.Tests
         }
 
         [Fact]
-        public static void CustomDictionaryConverter()
-        {
-            const string json = @"{""Key1"":1,""Key2"":2}";
-
-            var options = new JsonSerializerOptions();
-            options.Converters.Add(new DictionaryConverter(10));
-
-            {
-                Dictionary<string, long> dictionary = JsonSerializer.Deserialize<Dictionary<string, long>>(json, options);
-                Assert.Equal(11, dictionary["Key1"]);
-                Assert.Equal(12, dictionary["Key2"]);
-
-                string jsonSerialized = JsonSerializer.Serialize(dictionary, options);
-                Assert.Equal(json, jsonSerialized);
-            }
-        }
-
-        [Fact]
         public static void CustomDictionaryConverterContravariant()
         {
             const string Json = @"{""Key1"":1,""Key2"":2}";
 
             var options = new JsonSerializerOptions();
-            options.Converters.Add(new ConverterForIDictionary(10));
+            options.Converters.Add(new DictionaryConverterForIDictionary(10));
 
             Dictionary<string, long> dictionary = JsonSerializer.Deserialize<Dictionary<string, long>>(Json, options);
             Assert.Equal(11, dictionary["Key1"]);
@@ -108,7 +98,7 @@ namespace System.Text.Json.Serialization.Tests
             const string Json = @"{""MyInt"":32,""MyDictionary"":{""Key1"":1,""Key2"":2},""MyString"":""Hello""}";
 
             var options = new JsonSerializerOptions();
-            options.Converters.Add(new ConverterForIDictionary(10));
+            options.Converters.Add(new DictionaryConverterForIDictionary(10));
 
             ClassHavingDictionaryFieldWhichUsesCustomConverter dictionary = JsonSerializer.Deserialize<ClassHavingDictionaryFieldWhichUsesCustomConverter>(Json, options);
             Assert.Equal(11, dictionary.MyDictionary["Key1"]);
@@ -125,69 +115,5 @@ namespace System.Text.Json.Serialization.Tests
             public Dictionary<string, long> MyDictionary { get; set; }
             public string MyString { get; set; }
         }
-
-        private class ConverterForIDictionary : JsonConverter<IDictionary<string, long>>
-        {
-            private long _offset;
-
-            public ConverterForIDictionary(long offset)
-            {
-                _offset = offset;
-            }
-
-            public override bool CanConvert(Type typeToConvert)
-            {
-                return typeToConvert == typeof(Dictionary<string, long>);
-            }
-
-            public override IDictionary<string, long> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
-            {
-                if (reader.TokenType != JsonTokenType.StartObject)
-                {
-                    throw new JsonException();
-                }
-
-                var value = new Dictionary<string, long>();
-
-                while (reader.Read())
-                {
-                    if (reader.TokenType == JsonTokenType.EndObject)
-                    {
-                        return value;
-                    }
-
-                    if (reader.TokenType != JsonTokenType.PropertyName)
-                    {
-                        throw new JsonException();
-                    }
-
-                    string key = reader.GetString();
-
-                    reader.Read();
-                    if (reader.TokenType != JsonTokenType.Number)
-                    {
-                        throw new JsonException();
-                    }
-
-                    long longValue = reader.GetInt64() + _offset;
-
-                    value.Add(key, longValue);
-                }
-
-                throw new JsonException();
-            }
-
-            public override void Write(Utf8JsonWriter writer, IDictionary<string, long> value, JsonSerializerOptions options)
-            {
-                writer.WriteStartObject();
-
-                foreach (KeyValuePair<string, long> item in value)
-                {
-                    writer.WriteNumber(item.Key, item.Value - _offset);
-                }
-
-                writer.WriteEndObject();
-            }
-        }
     }
 }
diff --git a/src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.DictionaryGuidConverter.cs b/src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.DictionaryGuidConverter.cs
new file mode 100644 (file)
index 0000000..f085da7
--- /dev/null
@@ -0,0 +1,186 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Reflection;
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+    public static partial class CustomConverterTests
+    {
+        /// <summary>
+        /// Demonstrates a <see cref="Dictionary{Guid, TValue}"> converter using a JSON object with property names representing keys.
+        /// Sample JSON for <see cref="Dictionary{Guid, object}">: {"2E6E1787-1874-49BF-91F1-0F65CCB6C161":{}}
+        /// </summary>
+        internal sealed class DictionaryGuidConverter : JsonConverterFactory
+        {
+            public override bool CanConvert(Type typeToConvert)
+            {
+                if (!typeToConvert.IsGenericType)
+                {
+                    return false;
+                }
+
+                if (typeToConvert.GetGenericTypeDefinition() != typeof(Dictionary<,>))
+                {
+                    return false;
+                }
+
+                return (typeToConvert.GetGenericArguments()[0] == typeof(Guid));
+            }
+
+            public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options)
+            {
+                Type valueType = type.GetGenericArguments()[1];
+
+                JsonConverter converter = (JsonConverter)Activator.CreateInstance(
+                    typeof(DictionaryGuidConverterInner<>).MakeGenericType(new Type[] { valueType }),
+                    BindingFlags.Instance | BindingFlags.Public,
+                    binder: null,
+                    args: new object[] { options },
+                    culture: null);
+
+                return converter;
+            }
+
+            private class DictionaryGuidConverterInner<TValue> : JsonConverter<Dictionary<Guid, TValue>>
+            {
+                private readonly JsonConverter<TValue> _valueConverter;
+
+                public DictionaryGuidConverterInner(JsonSerializerOptions options)
+                {
+                    // For performance, use the existing converter if available.
+                    _valueConverter = (JsonConverter<TValue>)options.GetConverter(typeof(TValue));
+                }
+
+                public override Dictionary<Guid, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+                {
+                    if (reader.TokenType != JsonTokenType.StartObject)
+                    {
+                        throw new JsonException();
+                    }
+
+                    Dictionary<Guid, TValue> value = new Dictionary<Guid, TValue>();
+
+                    while (reader.Read())
+                    {
+                        if (reader.TokenType == JsonTokenType.EndObject)
+                        {
+                            return value;
+                        }
+
+                        // Get the key.
+                        if (reader.TokenType != JsonTokenType.PropertyName)
+                        {
+                            throw new JsonException();
+                        }
+
+                        string propertyName = reader.GetString();
+
+                        // Parse guid in "D" format: 00000000-0000-0000-0000-00000000000
+                        if (!Guid.TryParseExact(propertyName, format:"D", out Guid result))
+                        {
+                            throw new JsonException($"Unable to convert \"{propertyName}\" to a Guid.");
+                        }
+
+                        // Get the value.
+                        TValue v;
+                        if (_valueConverter != null)
+                        {
+                            reader.Read();
+                            v = _valueConverter.Read(ref reader, typeof(TValue), options);
+                        }
+                        else
+                        {
+                            v = JsonSerializer.Deserialize<TValue>(ref reader, options);
+                        }
+
+                        // Add to dictionary.
+                        value.Add(result, v);
+                    }
+
+                    throw new JsonException();
+                }
+
+                public override void Write(Utf8JsonWriter writer, Dictionary<Guid, TValue> value, JsonSerializerOptions options)
+                {
+                    writer.WriteStartObject();
+
+                    foreach (KeyValuePair<Guid, TValue> kvp in value)
+                    {
+                        writer.WritePropertyName(kvp.Key.ToString());
+
+                        if (_valueConverter != null)
+                        {
+                            _valueConverter.Write(writer, kvp.Value, options);
+                        }
+                        else
+                        {
+                            JsonSerializer.Serialize(writer, kvp.Value, options);
+                        }
+                    }
+
+                    writer.WriteEndObject();
+                }
+            }
+        }
+
+        [Fact]
+        public static void GuidToStringConverter()
+        {
+            Guid guid1 = Guid.NewGuid();
+            Guid guid2 = Guid.NewGuid();
+
+            string json = $"{{\"{guid1}\":\"One\",\"{guid2}\":\"Two\"}}";
+
+            var options = new JsonSerializerOptions();
+            options.Converters.Add(new DictionaryGuidConverter());
+
+            Dictionary<Guid, string> dictionary = JsonSerializer.Deserialize<Dictionary<Guid, string>>(json, options);
+            Assert.Equal("One", dictionary[guid1]);
+            Assert.Equal("Two", dictionary[guid2]);
+
+            string jsonSerialized = JsonSerializer.Serialize(dictionary, options);
+            Assert.Equal(json, jsonSerialized);
+        }
+
+        [Fact]
+        public static void GuidToEntityConverter()
+        {
+            Guid guid1 = Guid.NewGuid();
+            Guid guid2 = Guid.NewGuid();
+
+            Entity entity1 = new Entity();
+            entity1.Value = "entity1";
+
+            Entity entity2 = new Entity();
+            entity2.Value = "entity2";
+
+            var dictionary = new Dictionary<Guid, Entity>
+            {
+                [guid1] = entity1,
+                [guid2] = entity2,
+            };
+
+            void Verify()
+            {
+                Assert.Equal("entity1", dictionary[guid1].Value);
+                Assert.Equal("entity2", dictionary[guid2].Value);
+            }
+
+            // Verify baseline.
+            Verify();
+
+            var options = new JsonSerializerOptions();
+            options.Converters.Add(new DictionaryGuidConverter());
+
+            string json = JsonSerializer.Serialize(dictionary, options);
+            dictionary = JsonSerializer.Deserialize<Dictionary<Guid, Entity>>(json, options);
+
+            // Verify.
+            Verify();
+        }
+    }
+}
diff --git a/src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.DictionaryInt32StringConverter.cs b/src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.DictionaryInt32StringConverter.cs
new file mode 100644 (file)
index 0000000..0b4dbf8
--- /dev/null
@@ -0,0 +1,78 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+    public static partial class CustomConverterTests
+    {
+        /// <summary>
+        /// Demonstrates a <see cref="Dictionary{int, string}"> converter using a JSON object with property names representing keys.
+        /// Sample JSON: {"1":"ValueOne","2":"ValueTwo"}
+        /// </summary>
+        internal class DictionaryInt32StringConverter : JsonConverter<Dictionary<int, string>>
+        {
+            public override Dictionary<int, string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+            {
+                if (reader.TokenType != JsonTokenType.StartObject)
+                {
+                    throw new JsonException();
+                }
+
+                var value = new Dictionary<int, string>();
+
+                while (reader.Read())
+                {
+                    if (reader.TokenType == JsonTokenType.EndObject)
+                    {
+                        return value;
+                    }
+
+                    string keyAsString = reader.GetString();
+                    if (!int.TryParse(keyAsString, out int keyAsInt))
+                    {
+                        throw new JsonException($"Unable to convert \"{keyAsString}\" to System.Int32.");
+                    }
+
+                    reader.Read();
+                    string itemValue = reader.GetString();
+
+                    value.Add(keyAsInt, itemValue);
+                }
+
+                throw new JsonException();
+            }
+
+            public override void Write(Utf8JsonWriter writer, Dictionary<int, string> value, JsonSerializerOptions options)
+            {
+                writer.WriteStartObject();
+
+                foreach (KeyValuePair<int, string> item in value)
+                {
+                    writer.WriteString(item.Key.ToString(), item.Value);
+                }
+
+                writer.WriteEndObject();
+            }
+        }
+
+        [Fact]
+        public static void IntToStringDictionaryObjectConverter()
+        {
+            const string json = @"{""1"":""ValueOne"",""2"":""ValueTwo""}";
+
+            var options = new JsonSerializerOptions();
+            options.Converters.Add(new DictionaryInt32StringConverter());
+
+            Dictionary<int, string> dictionary = JsonSerializer.Deserialize<Dictionary<int, string>>(json, options);
+            Assert.Equal("ValueOne", dictionary[1]);
+            Assert.Equal("ValueTwo", dictionary[2]);
+
+            string jsonSerialized = JsonSerializer.Serialize(dictionary, options);
+            Assert.Equal(json, jsonSerialized);
+        }
+    }
+}
diff --git a/src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.DictionaryInt32StringKeyValueConverter.cs b/src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.DictionaryInt32StringKeyValueConverter.cs
new file mode 100644 (file)
index 0000000..f057360
--- /dev/null
@@ -0,0 +1,85 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+    /// <summary>
+    /// Demonstrates a <see cref="Dictionary{int, string}"> converter using a JSON array containing KeyValuePair objects.
+    /// Sample JSON: [{"Key":1,"Value":"One"},{"Key":2,"Value":"Two"}]
+    /// </summary>
+    internal class DictionaryInt32StringKeyValueConverter : JsonConverter<Dictionary<int, string>>
+    {
+        private JsonConverter<KeyValuePair<int, string>> _intToStringConverter;
+
+        public DictionaryInt32StringKeyValueConverter(JsonSerializerOptions options)
+        {
+            if (options == null)
+            {
+                throw new ArgumentNullException(nameof(options));
+            }
+
+            _intToStringConverter = (JsonConverter<KeyValuePair<int, string>>)options.GetConverter(typeof(KeyValuePair<int, string>));
+
+            // KeyValuePair<> converter is built-in.
+            Debug.Assert(_intToStringConverter != null);
+        }
+
+        public override Dictionary<int, string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+        {
+            if (reader.TokenType != JsonTokenType.StartArray)
+            {
+                throw new JsonException();
+            }
+
+            var value = new Dictionary<int, string>();
+
+            while (reader.Read())
+            {
+                if (reader.TokenType == JsonTokenType.EndArray)
+                {
+                    return value;
+                }
+
+                KeyValuePair<int, string> kvpair = _intToStringConverter.Read(ref reader, typeToConvert, options);
+
+                value.Add(kvpair.Key, kvpair.Value);
+            }
+
+            throw new JsonException();
+        }
+
+        public override void Write(Utf8JsonWriter writer, Dictionary<int, string> value, JsonSerializerOptions options)
+        {
+            writer.WriteStartArray();
+
+            foreach (KeyValuePair<int, string> item in value)
+            {
+                _intToStringConverter.Write(writer, item, options);
+            }
+
+            writer.WriteEndArray();
+        }
+    }
+
+    [Fact]
+    public static void Int32StringKeyValueArrayConverter()
+    {
+        const string json = @"[{""Key"":1,""Value"":""ValueOne""},{""Key"":2,""Value"":""ValueTwo""}]";
+
+        var options = new JsonSerializerOptions();
+        options.Converters.Add(new DictionaryInt32StringKeyValueConverter(options));
+
+        Dictionary<int, string> dictionary = JsonSerializer.Deserialize<Dictionary<int, string>>(json, options);
+        Assert.Equal("ValueOne", dictionary[1]);
+        Assert.Equal("ValueTwo", dictionary[2]);
+
+        string jsonSerialized = JsonSerializer.Serialize(dictionary, options);
+        Assert.Equal(json, jsonSerialized);
+    }
+}
diff --git a/src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.DictionaryKeyValueConverter.cs b/src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests.DictionaryKeyValueConverter.cs
new file mode 100644 (file)
index 0000000..5873eb6
--- /dev/null
@@ -0,0 +1,220 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+    public static partial class CustomConverterTests
+    {
+        /// <summary>
+        /// Demonstrates a <see cref="Dictionary{TKey, TValue}"> converter using a JSON array containing KeyValuePair objects.
+        /// Sample JSON for <see cref="Dictionary{int, string}">: [{"Key":1,"Value":"One"},{"Key":2,"Value":"Two"}]
+        /// </summary>
+        internal sealed class DictionaryKeyValueConverter : JsonConverterFactory
+        {
+            public override bool CanConvert(Type typeToConvert)
+            {
+                if (!typeToConvert.IsGenericType)
+                {
+                    return false;
+                }
+
+                if (typeToConvert.GetGenericTypeDefinition() != typeof(Dictionary<,>))
+                {
+                    return false;
+                }
+
+                // Don't change semantics of Dictionary<string, TValue> which uses JSON properties (not array of KeyValuePairs).
+                Type keyType = typeToConvert.GetGenericArguments()[0];
+                if (keyType == typeof(string))
+                {
+                    return false;
+                }
+
+                return true;
+            }
+
+            public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options)
+            {
+                Type keyType = type.GetGenericArguments()[0];
+                Type valueType = type.GetGenericArguments()[1];
+
+                JsonConverter converter = (JsonConverter)Activator.CreateInstance(
+                    typeof(DictionaryKeyValueConverterInner<,>).MakeGenericType(new Type[] { keyType, valueType }),
+                    BindingFlags.Instance | BindingFlags.Public,
+                    binder: null,
+                    args: new object[] { options },
+                    culture: null);
+
+                return converter;
+            }
+
+            private class DictionaryKeyValueConverterInner<TKey, TValue> : JsonConverter<Dictionary<TKey, TValue>>
+            {
+                private readonly JsonConverter<KeyValuePair<TKey, TValue>> _converter;
+
+                public DictionaryKeyValueConverterInner(JsonSerializerOptions options)
+                {
+                    _converter = (JsonConverter<KeyValuePair<TKey, TValue>>)options.GetConverter(typeof(KeyValuePair<TKey, TValue>));
+
+                    // KeyValuePair<> converter is built-in.
+                    Debug.Assert(_converter != null);
+                }
+
+                public override Dictionary<TKey, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+                {
+                    if (reader.TokenType != JsonTokenType.StartArray)
+                    {
+                        throw new JsonException();
+                    }
+
+                    Dictionary<TKey, TValue> value = new Dictionary<TKey, TValue>();
+
+                    while (reader.Read())
+                    {
+                        if (reader.TokenType == JsonTokenType.EndArray)
+                        {
+                            return value;
+                        }
+
+                        KeyValuePair<TKey, TValue> kv = _converter.Read(ref reader, typeToConvert, options);
+                        value.Add(kv.Key, kv.Value);
+                    }
+
+                    throw new JsonException();
+                }
+
+                public override void Write(Utf8JsonWriter writer, Dictionary<TKey, TValue> value, JsonSerializerOptions options)
+                {
+                    writer.WriteStartArray();
+
+                    foreach (KeyValuePair<TKey, TValue> kvp in value)
+                    {
+                        _converter.Write(writer, kvp, options);
+                    }
+
+                    writer.WriteEndArray();
+                }
+            }
+        }
+
+        [Fact]
+        public static void IntStringKeyValuePairConverter()
+        {
+            const string json = @"[{""Key"":1,""Value"":""One""},{""Key"":2,""Value"":""Two""}]";
+
+            var options = new JsonSerializerOptions();
+            options.Converters.Add(new DictionaryKeyValueConverter());
+
+            Dictionary<int, string> dictionary = JsonSerializer.Deserialize<Dictionary<int, string>>(json, options);
+            Assert.Equal("One", dictionary[1]);
+            Assert.Equal("Two", dictionary[2]);
+
+            string jsonSerialized = JsonSerializer.Serialize(dictionary, options);
+            Assert.Equal(json, jsonSerialized);
+        }
+
+        [Fact]
+        public static void NestedDictionaryConversion()
+        {
+            const string json = @"[{""Key"":1,""Value"":[{""Key"":10,""Value"":11}]},{""Key"":2,""Value"":[{""Key"":20,""Value"":21}]}]";
+
+            var options = new JsonSerializerOptions();
+            options.Converters.Add(new DictionaryKeyValueConverter());
+
+            Dictionary<int, Dictionary<int, int>> dictionary = JsonSerializer.Deserialize<Dictionary<int, Dictionary<int, int>>>(json, options);
+            Assert.Equal(11, dictionary[1][10]);
+            Assert.Equal(21, dictionary[2][20]);
+
+            string jsonSerialized = JsonSerializer.Serialize(dictionary, options);
+            Assert.Equal(json, jsonSerialized);
+        }
+
+        private class Entity
+        {
+            public string Value { get; set; }
+        }
+
+        private enum MyEnum
+        {
+            One = 1
+        }
+
+        private class ClassWithDictionaries
+        {
+            public Dictionary<bool, Entity> BoolKey { get; set; }
+            public Dictionary<MyEnum, Entity> EnumKey { get; set; }
+            public Dictionary<Guid, Entity> GuidKey { get; set; }
+            public Dictionary<int, Entity> IntKey { get; set; }
+            public Dictionary<float, Entity> FloatKey { get; set; }
+            public Dictionary<double, Entity> DoubleKey { get; set; }
+            public Dictionary<string, Entity> StringKey { get; set; }
+        }
+
+        [Fact]
+        public static void AllPrimitivesConvertion()
+        {
+            ClassWithDictionaries obj;
+            Guid guid = Guid.NewGuid();
+
+            void Verify()
+            {
+                Assert.Equal("test", obj.BoolKey[true].Value);
+                Assert.Equal("test", obj.EnumKey[MyEnum.One].Value);
+                Assert.Equal("test", obj.GuidKey[guid].Value);
+                Assert.Equal("test", obj.IntKey[1].Value);
+                Assert.Equal("test", obj.FloatKey[1.34f].Value);
+                Assert.Equal("test", obj.DoubleKey[1.35].Value);
+                Assert.Equal("test", obj.StringKey["key"].Value);
+            }
+
+            var options = new JsonSerializerOptions();
+            options.Converters.Add(new DictionaryKeyValueConverter());
+
+            obj = new ClassWithDictionaries
+            {
+                BoolKey = new Dictionary<bool, Entity> { [true] = new Entity { Value = "test" } },
+                EnumKey = new Dictionary<MyEnum, Entity> { [MyEnum.One] = new Entity { Value = "test" } },
+                GuidKey = new Dictionary<Guid, Entity> { [guid] = new Entity { Value = "test" } },
+                DoubleKey = new Dictionary<double, Entity> { [1.35] = new Entity { Value = "test" } },
+                FloatKey = new Dictionary<float, Entity> { [1.34f] = new Entity { Value = "test" } },
+                IntKey = new Dictionary<int, Entity> { [1] = new Entity { Value = "test" } },
+
+                // String is actually handled by built-in converter, not the custom converter.
+                StringKey = new Dictionary<string, Entity> { ["key"] = new Entity { Value = "test" } },
+            };
+
+            // Verify baseline.
+            Verify();
+
+            string json = JsonSerializer.Serialize(obj, options);
+            obj = JsonSerializer.Deserialize<ClassWithDictionaries>(json, options);
+
+            // Verify.
+            Verify();
+        }
+
+        [Fact]
+        public static void EnumFail()
+        {
+            var options = new JsonSerializerOptions();
+            options.Converters.Add(new DictionaryKeyValueConverter());
+            options.Converters.Add(new JsonStringEnumConverter()); // Use string for Enum instead of int.
+
+            // Baseline.
+            Dictionary<MyEnum, int> dictionary = JsonSerializer.Deserialize<Dictionary<MyEnum, int>>(@"[{""Key"":""One"",""Value"":100}]", options);
+            Assert.Equal(100, dictionary[MyEnum.One]);
+
+            // Invalid JSON.
+            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<Dictionary<MyEnum, int>>(@"{x}", options));
+
+            // Invalid enum value.
+            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<Dictionary<MyEnum, int>>(@"[{""Key"":""BAD"",""Value"":100}]", options));
+        }
+    }
+}
index d0d875f..10926b1 100644 (file)
@@ -411,8 +411,6 @@ namespace System.Text.Json.Serialization.Tests
         {
             var options = new JsonSerializerOptions();
             options.Converters.Add(new CustomConverterTests.LongArrayConverter());
-            options.Converters.Add(new CustomConverterTests.DictionaryConverter(20));
-            GenericConverterTestHelper<Dictionary<string, long>>("DictionaryConverter", new Dictionary<string, long> { { "val1", 123 }, { "val2", 456 } }, "{\"val1\":103,\"val2\":436}", options);
             GenericConverterTestHelper<long[]>("LongArrayConverter", new long[] { 1, 2, 3, 4 }, "\"1,2,3,4\"", options);
         }
 
index a2b5510..5e81a9e 100644 (file)
     <Compile Include="Serialization\CustomConverterTests.BadConverters.cs" />
     <Compile Include="Serialization\CustomConverterTests.cs" />
     <Compile Include="Serialization\CustomConverterTests.DerivedTypes.cs" />
-    <Compile Include="Serialization\CustomConverterTests.Dictionary.cs" />
+    <Compile Include="Serialization\CustomConverterTests.DictionaryConverterForIDictionary.cs" />
+    <Compile Include="Serialization\CustomConverterTests.DictionaryInt32StringConverter.cs" />
+    <Compile Include="Serialization\CustomConverterTests.DictionaryKeyValueConverter.cs" />
+    <Compile Include="Serialization\CustomConverterTests.DictionaryGuidConverter.cs" />
     <Compile Include="Serialization\CustomConverterTests.Enum.cs" />
     <Compile Include="Serialization\CustomConverterTests.Exceptions.cs" />
     <Compile Include="Serialization\CustomConverterTests.Int32.cs" />
     <Compile Include="JsonObjectTests.cs" />
     <Compile Include="JsonStringTests.cs" />
   </ItemGroup>
+  <ItemGroup>
+    <None Include="Serialization\CustomConverterTests.DictionaryInt32StringKeyValueConverter.cs" />
+  </ItemGroup>
 </Project>