Do not use DictionaryKeyPolicy on deserialization (dotnet/corefx#39392)
authorLayomi Akinrinade <laakinri@microsoft.com>
Fri, 12 Jul 2019 22:55:11 +0000 (18:55 -0400)
committerEric StJohn <ericstj@microsoft.com>
Fri, 12 Jul 2019 22:55:10 +0000 (15:55 -0700)
* Do not use DictionaryKeyPolicy on deserialization

* Update policy doc

Commit migrated from https://github.com/dotnet/corefx/commit/ba8641a3cdfc9372597e4dbd5ad62be4cbc32b8b

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs
src/libraries/System.Text.Json/tests/Serialization/DictionaryTests.KeyPolicy.cs

index 987dc80..ced83b4 100644 (file)
@@ -26,20 +26,6 @@ namespace System.Text.Json
             if ((state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructible) &&
                 state.Current.JsonClassInfo.DataExtensionProperty != state.Current.JsonPropertyInfo)
             {
-                string keyName = reader.GetString();
-
-                if (options.DictionaryKeyPolicy != null)
-                {
-                    keyName = options.DictionaryKeyPolicy.ConvertName(keyName);
-
-                    if (keyName == null)
-                    {
-                        ThrowHelper.ThrowInvalidOperationException_SerializerDictionaryKeyNull(options.DictionaryKeyPolicy.GetType());
-                    }
-
-                    keyName = options.DictionaryKeyPolicy.ConvertName(keyName);
-                }
-
                 if (state.Current.IsDictionary || state.Current.IsIDictionaryConstructible)
                 {
                     state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.PolicyProperty;
@@ -51,7 +37,7 @@ namespace System.Text.Json
                     state.Current.IsIDictionaryConstructible ||
                     (state.Current.IsIDictionaryConstructibleProperty && state.Current.JsonPropertyInfo != null));
 
-                state.Current.KeyName = keyName;
+                state.Current.KeyName = reader.GetString();
             }
             else
             {
index f326586..c3e3a74 100644 (file)
@@ -97,6 +97,7 @@ namespace System.Text.Json
         /// </summary>
         /// <remarks>
         /// This property can be set to <see cref="JsonNamingPolicy.CamelCase"/> to specify a camel-casing policy.
+        /// It is not used when deserializing.
         /// </remarks>
         public JsonNamingPolicy DictionaryKeyPolicy
         {
index cf3d3fc..65c97b1 100644 (file)
@@ -16,17 +16,36 @@ namespace System.Text.Json.Serialization.Tests
         {
             var options = new JsonSerializerOptions
             {
-                DictionaryKeyPolicy = JsonNamingPolicy.CamelCase
+                DictionaryKeyPolicy = JsonNamingPolicy.CamelCase // e.g. Key1 -> key1.
             };
 
             const string JsonString = @"[{""Key1"":1,""Key2"":2},{""Key1"":3,""Key2"":4}]";
-            Dictionary<string, int>[] obj = JsonSerializer.Deserialize<Dictionary<string, int>[]>(JsonString, options);
+
+            // Without key policy, deserialize keys as they are.
+            Dictionary<string, int>[] obj = JsonSerializer.Deserialize<Dictionary<string, int>[]>(JsonString);
 
             Assert.Equal(2, obj.Length);
-            Assert.Equal(1, obj[0]["key1"]);
-            Assert.Equal(2, obj[0]["key2"]);
-            Assert.Equal(3, obj[1]["key1"]);
-            Assert.Equal(4, obj[1]["key2"]);
+
+            Assert.Equal(2, obj[0].Count);
+            Assert.Equal(1, obj[0]["Key1"]);
+            Assert.Equal(2, obj[0]["Key2"]);
+
+            Assert.Equal(2, obj[1].Count);
+            Assert.Equal(3, obj[1]["Key1"]);
+            Assert.Equal(4, obj[1]["Key2"]);
+
+            // Ensure we ignore key policy and deserialize keys as they are.
+            obj = JsonSerializer.Deserialize<Dictionary<string, int>[]>(JsonString, options);
+
+            Assert.Equal(2, obj.Length);
+
+            Assert.Equal(2, obj[0].Count);
+            Assert.Equal(1, obj[0]["Key1"]);
+            Assert.Equal(2, obj[0]["Key2"]);
+
+            Assert.Equal(2, obj[1].Count);
+            Assert.Equal(3, obj[1]["Key1"]);
+            Assert.Equal(4, obj[1]["Key2"]);
         }
 
         [Fact]
@@ -34,7 +53,7 @@ namespace System.Text.Json.Serialization.Tests
         {
             var options = new JsonSerializerOptions()
             {
-                DictionaryKeyPolicy = JsonNamingPolicy.CamelCase
+                DictionaryKeyPolicy = JsonNamingPolicy.CamelCase // e.g. Key1 -> key1.
             };
 
             Dictionary<string, int>[] obj = new Dictionary<string, int>[]
@@ -60,11 +79,17 @@ namespace System.Text.Json.Serialization.Tests
         {
             var options = new JsonSerializerOptions
             {
-                DictionaryKeyPolicy = new UppercaseNamingPolicy()
+                DictionaryKeyPolicy = new UppercaseNamingPolicy() // e.g. myint -> MYINT.
             };
 
-            Dictionary<string, int> obj = JsonSerializer.Deserialize<Dictionary<string, int>>(@"{""myint"":1}", options);
-            Assert.Equal(1, obj["MYINT"]);
+
+            // Without key policy, deserialize keys as they are.
+            Dictionary<string, int> obj = JsonSerializer.Deserialize<Dictionary<string, int>>(@"{""myint"":1}");
+            Assert.Equal(1, obj["myint"]);
+
+            // Ensure we ignore key policy and deserialize keys as they are.
+            obj = JsonSerializer.Deserialize<Dictionary<string, int>>(@"{""myint"":1}", options);
+            Assert.Equal(1, obj["myint"]);
         }
 
         [Fact]
@@ -72,7 +97,7 @@ namespace System.Text.Json.Serialization.Tests
         {
             var options = new JsonSerializerOptions
             {
-                DictionaryKeyPolicy = new UppercaseNamingPolicy()
+                DictionaryKeyPolicy = new UppercaseNamingPolicy() // e.g. myint -> MYINT.
             };
 
             Dictionary<string, int> obj = new Dictionary<string, int> { { "myint1", 1 }, { "myint2", 2 } };
@@ -97,30 +122,18 @@ namespace System.Text.Json.Serialization.Tests
                 DictionaryKeyPolicy = new NullNamingPolicy()
             };
 
-            // A policy that returns null is not allowed.
-            Assert.Throws<InvalidOperationException>(() => JsonSerializer.Deserialize<Dictionary<string, int>>(@"{""onlyKey"": 1}", options));
+            // A naming policy that returns null is not allowed.
             Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(new Dictionary<string, int> { { "onlyKey", 1 } }, options));
-        }
-
-        [Fact]
-        public static void KeyConflictDeserialize_LastValueWins()
-        {
-            var options = new JsonSerializerOptions
-            {
-                DictionaryKeyPolicy = JsonNamingPolicy.CamelCase
-            };
 
-            // The camel case policy resolves two keys to the same output key.
-            string json = @"{""myInt"":1,""MyInt"":2}";
-            Dictionary<string, int> obj = JsonSerializer.Deserialize<Dictionary<string, int>>(json, options);
+            // We don't use policy on deserialize, so we populate dictionary.
+            Dictionary<string, int> obj = JsonSerializer.Deserialize<Dictionary<string, int>>(@"{""onlyKey"": 1}", options);
 
-            // Check that the last value wins.
-            Assert.Equal(2, obj["myInt"]);
             Assert.Equal(1, obj.Count);
+            Assert.Equal(1, obj["onlyKey"]);
         }
 
         [Fact]
-        public static void KeyConflictSerialize_WriteAll()
+        public static void KeyConflict_Serialize_WriteAll()
         {
             var options = new JsonSerializerOptions
             {