Add additional skip tests and prevent reset of property index for perf (dotnet/corefx...
authorSteve Harter <steveharter@users.noreply.github.com>
Tue, 27 Aug 2019 16:06:59 +0000 (11:06 -0500)
committerGitHub <noreply@github.com>
Tue, 27 Aug 2019 16:06:59 +0000 (11:06 -0500)
Commit migrated from https://github.com/dotnet/corefx/commit/7cc4a04ed3bb9319ec2d80c186a22da3803fda23

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.cs
src/libraries/System.Text.Json/tests/Serialization/Array.ReadTests.cs
src/libraries/System.Text.Json/tests/Serialization/DictionaryTests.cs

index 5bdf3cf..23f0589 100644 (file)
@@ -82,7 +82,10 @@ namespace System.Text.Json
                         if (readStack.Current.Drain)
                         {
                             readStack.Pop();
-                            readStack.Current.EndObject();
+
+                            // Clear the current property in case it is a dictionary, since dictionaries must have EndProperty() called when completed.
+                            // A non-dictionary property can also have EndProperty() called when completed, although it is redundant.
+                            readStack.Current.EndProperty();
                         }
                         else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructible)
                         {
index 55bcecf..914393e 100644 (file)
@@ -442,10 +442,12 @@ namespace System.Text.Json.Serialization.Tests
 
         public class ClassWithMixedSetters
         {
-            public List<int> SkippedChild { get; }
-            public List<int> ParsedChild { get; set; }
-            public IEnumerable<int> AnotherSkippedChild { get; }
-            public IEnumerable<int> AnotherParsedChild { get; set; }
+            public List<int> SkippedChild1 { get; }
+            public List<int> ParsedChild1 { get; set; }
+            public IEnumerable<int> SkippedChild2 { get; }
+            public IEnumerable<int> ParsedChild2 { get; set; }
+            [JsonIgnore] public IEnumerable<int> SkippedChild3 { get; set; } // Note this has a setter.
+            public IEnumerable<int> ParsedChild3 { get; set; }
         }
 
         [Fact]
@@ -453,26 +455,31 @@ namespace System.Text.Json.Serialization.Tests
         {
             // Tests that the parser picks back up after skipping/draining ignored elements.
             string json = @"{
-                ""SkippedChild"": {},
-                ""ParsedChild"": [18],
+                ""SkippedChild1"": {},
+                ""ParsedChild1"": [1],
                 ""UnmatchedProp"": null,
-                ""AnotherSkippedChild"": [{""DrainProp1"":{}, ""DrainProp2"":{""SubProp"":0}}],
-                ""AnotherSkippedChild"": {},
-                ""AnotherParsedChild"": [18,20]
+                ""SkippedChild2"": [{""DrainProp1"":{}, ""DrainProp2"":{""SubProp"":0}}],
+                ""SkippedChild2"": {},
+                ""ParsedChild2"": [2,2],
+                ""SkippedChild3"": {},
+                ""ParsedChild3"": [3,3]
             }";
 
             ClassWithMixedSetters parsedObject = JsonSerializer.Deserialize<ClassWithMixedSetters>(json);
 
-            Assert.Null(parsedObject.SkippedChild);
+            Assert.Null(parsedObject.SkippedChild1);
 
-            Assert.NotNull(parsedObject.ParsedChild);
-            Assert.Equal(1, parsedObject.ParsedChild.Count);
-            Assert.Equal(18, parsedObject.ParsedChild[0]);
+            Assert.NotNull(parsedObject.ParsedChild1);
+            Assert.Equal(1, parsedObject.ParsedChild1.Count);
+            Assert.Equal(1, parsedObject.ParsedChild1[0]);
 
-            Assert.Null(parsedObject.AnotherSkippedChild);
+            Assert.Null(parsedObject.SkippedChild2);
 
-            Assert.NotNull(parsedObject.AnotherParsedChild);
-            Assert.True(parsedObject.AnotherParsedChild.SequenceEqual(new int[] { 18, 20 }));
+            Assert.NotNull(parsedObject.ParsedChild2);
+            Assert.True(parsedObject.ParsedChild2.SequenceEqual(new int[] { 2, 2 }));
+
+            Assert.NotNull(parsedObject.ParsedChild3);
+            Assert.True(parsedObject.ParsedChild3.SequenceEqual(new int[] { 3, 3 }));
         }
     }
 }
index d653c6d..d13fe7b 100644 (file)
@@ -936,41 +936,176 @@ namespace System.Text.Json.Serialization.Tests
             Assert.Equal(1, obj.MyImmutableDictionary.Count);
         }
 
-        public class ClassWithNoSetter
+        public class ClassWithIgnoredDictionary1
         {
-            public Dictionary<string, int> SkippedChild { get; }
-            public Dictionary<string, int> ParsedChild { get; set; }
-            public IDictionary<string, int> AnotherSkippedChild { get; }
-            public IDictionary<string, int> AnotherParsedChild { get; set; }
+            public Dictionary<string, int> Parsed1 { get; set; }
+            public Dictionary<string, int> Parsed2 { get; set; }
+            public Dictionary<string, int> Skipped3 { get; }
+        }
+
+        public class ClassWithIgnoredDictionary2
+        {
+            public IDictionary<string, int> Parsed1 { get; set; }
+            public IDictionary<string, int> Skipped2 { get; }
+            public IDictionary<string, int> Parsed3 { get; set; }
+        }
+
+        public class ClassWithIgnoredDictionary3
+        {
+            public Dictionary<string, int> Parsed1 { get; set; }
+            public Dictionary<string, int> Skipped2 { get; }
+            public Dictionary<string, int> Skipped3 { get; }
+        }
+
+        public class ClassWithIgnoredDictionary4
+        {
+            public Dictionary<string, int> Skipped1 { get; }
+            public Dictionary<string, int> Parsed2 { get; set; }
+            public Dictionary<string, int> Parsed3 { get; set; }
+        }
+
+        public class ClassWithIgnoredDictionary5
+        {
+            public Dictionary<string, int> Skipped1 { get; }
+            public Dictionary<string, int> Parsed2 { get; set; }
+            public Dictionary<string, int> Skipped3 { get; }
+        }
+
+        public class ClassWithIgnoredDictionary6
+        {
+            public Dictionary<string, int> Skipped1 { get; }
+            public Dictionary<string, int> Skipped2 { get; }
+            public Dictionary<string, int> Parsed3 { get; set; }
+        }
+
+        public class ClassWithIgnoredDictionary7
+        {
+            public Dictionary<string, int> Skipped1 { get; }
+            public Dictionary<string, int> Skipped2 { get; }
+            public Dictionary<string, int> Skipped3 { get; }
+        }
+
+        public class ClassWithIgnoredIDictionary
+        {
+            public IDictionary<string, int> Parsed1 { get; set; }
+            public IDictionary<string, int> Skipped2 { get; }
+            public IDictionary<string, int> Parsed3 { get; set; }
+        }
+
+        public class ClassWithIgnoreAttributeDictionary
+        {
+            public Dictionary<string, int> Parsed1 { get; set; }
+            [JsonIgnore] public Dictionary<string, int> Skipped2 { get; set; } // Note this has a setter.
+            public Dictionary<string, int> Parsed3 { get; set; }
+        }
+
+        public class ClassWithIgnoredImmutableDictionary
+        {
+            public ImmutableDictionary<string, int> Parsed1 { get; set; }
+            public ImmutableDictionary<string, int> Skipped2 { get; }
+            public ImmutableDictionary<string, int> Parsed3 { get; set; }
         }
 
         [Fact]
-        public static void ClassWithNoSetterAndValidProperty()
+        public static void IgnoredMembers()
+        {
+            // Verify all combinations of 3 properties with at least one ignore.
+            VerifyIgnore<ClassWithIgnoredDictionary1>(false, false, true);
+            VerifyIgnore<ClassWithIgnoredDictionary2>(false, true, false);
+            VerifyIgnore<ClassWithIgnoredDictionary3>(false, true, true);
+            VerifyIgnore<ClassWithIgnoredDictionary4>(true, false, false);
+            VerifyIgnore<ClassWithIgnoredDictionary5>(true, false, true);
+            VerifyIgnore<ClassWithIgnoredDictionary6>(true, true, false);
+            VerifyIgnore<ClassWithIgnoredDictionary7>(true, true, true);
+
+            // Verify single case for IDictionary, [Ignore] and ImmutableDictionary.
+            VerifyIgnore<ClassWithIgnoredIDictionary>(false, true, false, true);
+            VerifyIgnore<ClassWithIgnoreAttributeDictionary>(false, true, false, true);
+            VerifyIgnore<ClassWithIgnoredImmutableDictionary>(false, true, false, true);
+        }
+
+        private static void VerifyIgnore<T>(bool skip1, bool skip2, bool skip3, bool addMissing = false)
         {
             // Tests that the parser picks back up after skipping/draining ignored elements.
-            string json = @"{
-                ""SkippedChild"": {},
-                ""ParsedChild"": {""Key1"":18},
-                ""UnmatchedProp"": null,
-                ""AnotherSkippedChild"": {""DrainProp1"":{}, ""DrainProp2"":{""SubProp"":0}},
-                ""AnotherSkippedChild"": {},
-                ""AnotherParsedChild"": {""Key1"":18, ""Key2"":20}
-            }";
+            StringBuilder json = new StringBuilder(@"{");
 
-            ClassWithNoSetter parsedObject = JsonSerializer.Deserialize<ClassWithNoSetter>(json);
+            if (addMissing)
+            {
+                json.Append(@"""MissingProp1"": {},");
+            }
 
-            Assert.Null(parsedObject.SkippedChild);
+            if (skip1)
+            {
+                json.Append(@"""Skipped1"":{},");
+            }
+            else
+            {
+                json.Append(@"""Parsed1"":{""Key"":1},");
+            }
+
+            if (addMissing)
+            {
+                json.Append(@"""MissingProp2"": null,");
+            }
+
+            if (skip2)
+            {
+                json.Append(@"""Skipped2"":{},");
+            }
+            else
+            {
+                json.Append(@"""Parsed2"":{""Key"":2},");
+            }
+
+            if (addMissing)
+            {
+                json.Append(@"""MissingProp3"": {""ABC"":{}},");
+            }
+
+            if (skip3)
+            {
+                json.Append(@"""Skipped3"":{}}");
+            }
+            else
+            {
+                json.Append(@"""Parsed3"":{""Key"":3}}");
+            }
 
-            Assert.NotNull(parsedObject.ParsedChild);
-            Assert.Equal(1, parsedObject.ParsedChild.Count);
-            Assert.Equal(18, parsedObject.ParsedChild["Key1"]);
+            // Deserialize and verify.
 
-            Assert.Null(parsedObject.AnotherSkippedChild);
+            T obj = JsonSerializer.Deserialize<T>(json.ToString());
 
-            Assert.NotNull(parsedObject.AnotherParsedChild);
-            Assert.Equal(2, parsedObject.AnotherParsedChild.Count);
-            Assert.Equal(18, parsedObject.AnotherParsedChild["Key1"]);
-            Assert.Equal(20, parsedObject.AnotherParsedChild["Key2"]);
+            IDictionary<string, int> GetProperty(string propertyName)
+            {
+                return (IDictionary<string, int>)obj.GetType().GetProperty(propertyName).GetValue(obj);
+            }
+
+            if (skip1)
+            {
+                Assert.Null(GetProperty("Skipped1"));
+            }
+            else
+            {
+                Assert.Equal(1, GetProperty("Parsed1")["Key"]);
+            }
+
+            if (skip2)
+            {
+                Assert.Null(GetProperty("Skipped2"));
+            }
+            else
+            {
+                Assert.Equal(2, GetProperty("Parsed2")["Key"]);
+            }
+
+            if (skip3)
+            {
+                Assert.Null(GetProperty("Skipped3"));
+            }
+            else
+            {
+                Assert.Equal(3, GetProperty("Parsed3")["Key"]);
+            }
         }
 
         public class ClassWithPopulatedDictionaryAndSetter