supporting duplicate using hashsets (dotnet/corefx#38705)
authorAnirudh Agnihotry <anirudhagnihotry098@gmail.com>
Thu, 20 Jun 2019 17:47:42 +0000 (10:47 -0700)
committerGitHub <noreply@github.com>
Thu, 20 Jun 2019 17:47:42 +0000 (10:47 -0700)
Commit migrated from https://github.com/dotnet/corefx/commit/24e81ed6260fabf1211510a93c24385204f08323

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs
src/libraries/System.Text.Json/tests/Serialization/DictionaryTests.cs

index 7488a8fbebb42f0051cfe556f884a18814a7810f..610e6ab1aa61b2ceb2c242fe9a13c8d0290a903a 100644 (file)
@@ -46,7 +46,7 @@ namespace System.Text.Json
             // Set the sorted property cache. Overwrite any existing cache which can occur in multi-threaded cases.
             if (frame.PropertyRefCache != null)
             {
-                List<PropertyRef> cache = frame.PropertyRefCache;
+                HashSet<PropertyRef> cache = frame.PropertyRefCache;
 
                 // Add any missing properties. This creates a consistent cache count which is important for
                 // the loop in GetProperty() when there are multiple threads in a race conditions each generating
@@ -56,29 +56,17 @@ namespace System.Text.Json
                     for (int iProperty = 0; iProperty < _propertyRefs.Count; iProperty++)
                     {
                         PropertyRef propertyRef = _propertyRefs[iProperty];
-                        bool found = false;
-                        int iCacheProperty = 0;
-
-                        for (; iCacheProperty < cache.Count; iCacheProperty++)
-                        {
-                            if (IsPropertyRefEqual(ref propertyRef, cache[iCacheProperty]))
-                            {
-                                // The property is already cached, skip to the next property.
-                                found = true;
-                                break;
-                            }
-                        }
-
-                        if (found == false)
-                        {
-                            cache.Add(propertyRef);
-                            break;
-                        }
+                        // Cache the missing property or override the existing property.
+                        cache.Add(propertyRef);
                     }
                 }
 
                 Debug.Assert(cache.Count == _propertyRefs.Count);
-                _propertyRefsSorted = cache.ToArray();
+                if (_propertyRefsSorted == null || _propertyRefsSorted.Length < cache.Count)
+                {
+                    _propertyRefsSorted = new PropertyRef[cache.Count];
+                }
+                cache.CopyTo(_propertyRefsSorted);
                 frame.PropertyRefCache = null;
             }
         }
@@ -272,7 +260,7 @@ namespace System.Text.Json
                 if (propertyIndex == 0 && frame.PropertyRefCache == null)
                 {
                     // Create the temporary list on first property access to prevent a partially filled List.
-                    frame.PropertyRefCache = new List<PropertyRef>();
+                    frame.PropertyRefCache = new HashSet<PropertyRef>();
                 }
 
                 if (info != null)
index 081b784e0ca73d90a8186317d50cbfe1634f1a62..a20a572a7d9dd61658e599b6e9046df561727693 100644 (file)
@@ -38,7 +38,7 @@ namespace System.Text.Json
 
         // For performance, we order the properties by the first deserialize and PropertyIndex helps find the right slot quicker.
         public int PropertyIndex;
-        public List<PropertyRef> PropertyRefCache;
+        public HashSet<PropertyRef> PropertyRefCache;
 
         // The current JSON data for a property does not match a given POCO, so ignore the property (recursively).
         public bool Drain;
index 159bb2423cf3e7ecf2e605227f00eeeb2855645e..b465a99123248137ca02972d3b24c4dc7bbacf6f 100644 (file)
@@ -799,6 +799,28 @@ namespace System.Text.Json.Serialization.Tests
             Assert.Null(dictionaryObject["Hello"]);
         }
 
+        [Fact]
+        public static void DeserializeDictionaryWithDuplicateProperties()
+        {
+            PocoDuplicate foo = JsonSerializer.Parse<PocoDuplicate>(@"{""BoolProperty"": false, ""BoolProperty"": true}");
+            Assert.True(foo.BoolProperty);
+
+            foo = JsonSerializer.Parse<PocoDuplicate>(@"{""BoolProperty"": false, ""IntProperty"" : 1, ""BoolProperty"": true , ""IntProperty"" : 2}");
+            Assert.True(foo.BoolProperty);
+            Assert.Equal(2, foo.IntProperty);
+
+            foo = JsonSerializer.Parse<PocoDuplicate>(@"{""DictProperty"" : {""a"" : ""b"", ""c"" : ""d""},""DictProperty"" : {""b"" : ""b"", ""c"" : ""e""}}");
+            Assert.Equal(3, foo.DictProperty.Count);
+            Assert.Equal("e", foo.DictProperty["c"]);
+        }
+
+        public class PocoDuplicate
+        {
+            public bool BoolProperty { get; set; }
+            public int IntProperty { get; set; }
+            public Dictionary<string, string> DictProperty { get; set; }
+        }
+
         [Fact]
         public static void ClassWithNoSetter()
         {