From 4c3a259a90f740e1f515d4ae22c34515ac1b5e2e Mon Sep 17 00:00:00 2001 From: Anirudh Agnihotry Date: Thu, 20 Jun 2019 10:47:42 -0700 Subject: [PATCH] supporting duplicate using hashsets (dotnet/corefx#38705) Commit migrated from https://github.com/dotnet/corefx/commit/24e81ed6260fabf1211510a93c24385204f08323 --- .../Text/Json/Serialization/JsonClassInfo.cs | 30 ++++++------------- .../Text/Json/Serialization/ReadStackFrame.cs | 2 +- .../tests/Serialization/DictionaryTests.cs | 22 ++++++++++++++ 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs index 7488a8fbebb..610e6ab1aa6 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs @@ -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 cache = frame.PropertyRefCache; + HashSet 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(); + frame.PropertyRefCache = new HashSet(); } if (info != null) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs index 081b784e0ca..a20a572a7d9 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs @@ -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 PropertyRefCache; + public HashSet PropertyRefCache; // The current JSON data for a property does not match a given POCO, so ignore the property (recursively). public bool Drain; diff --git a/src/libraries/System.Text.Json/tests/Serialization/DictionaryTests.cs b/src/libraries/System.Text.Json/tests/Serialization/DictionaryTests.cs index 159bb2423cf..b465a991232 100644 --- a/src/libraries/System.Text.Json/tests/Serialization/DictionaryTests.cs +++ b/src/libraries/System.Text.Json/tests/Serialization/DictionaryTests.cs @@ -799,6 +799,28 @@ namespace System.Text.Json.Serialization.Tests Assert.Null(dictionaryObject["Hello"]); } + [Fact] + public static void DeserializeDictionaryWithDuplicateProperties() + { + PocoDuplicate foo = JsonSerializer.Parse(@"{""BoolProperty"": false, ""BoolProperty"": true}"); + Assert.True(foo.BoolProperty); + + foo = JsonSerializer.Parse(@"{""BoolProperty"": false, ""IntProperty"" : 1, ""BoolProperty"": true , ""IntProperty"" : 2}"); + Assert.True(foo.BoolProperty); + Assert.Equal(2, foo.IntProperty); + + foo = JsonSerializer.Parse(@"{""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 DictProperty { get; set; } + } + [Fact] public static void ClassWithNoSetter() { -- 2.34.1