From 9a141438b0fdc425b5d1e856cfe163b750974856 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Fri, 10 May 2019 14:53:30 +0200 Subject: [PATCH 1/3] fix bug complex properties --- .../JsonSerializer.Read.HandleObject.cs | 15 +++- .../JsonSerializer.Write.HandleObject.cs | 6 ++ .../Text/Json/Serialization/ReadStackFrame.cs | 2 +- .../Serialization/PropertyVisibilityTests.cs | 79 ++++++++++++++++++- 4 files changed, 96 insertions(+), 6 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleObject.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleObject.cs index 425c4d78f65d..a9e0e256fddc 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleObject.cs @@ -60,9 +60,18 @@ private static void HandleStartObject(JsonSerializerOptions options, ref Utf8Jso else { // Nested object. - Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; - state.Push(); - state.Current.Initialize(objType, options); + if (!state.Current.JsonPropertyInfo.ShouldDeserialize) + { + state.Push(); + state.Current.Drain = true; + return; + } + else + { + Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; + state.Push(); + state.Current.Initialize(objType, options); + } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs index 1daa62da2db0..9197caab3092 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs @@ -102,6 +102,12 @@ private static bool HandleObject( // A property that returns an object. if (!obtainedValue) { + if (!jsonPropertyInfo.ShouldSerialize) + { + // Ignore writing this property. + state.Current.NextProperty(); + return true; + } currentValue = jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue); } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs index 5c3ab721c1a9..626e37532727 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs @@ -33,7 +33,7 @@ internal struct ReadStackFrame public int PropertyIndex; public List PropertyRefCache; - // The current JSON data for a property does not match a given POCO, so ignore the property (recursively). + // The current JSON data for a property does not match a given POCO or ShouldDeserialize = false, so ignore the property (recursively). public bool Drain; public bool IsDictionary => JsonClassInfo.ClassType == ClassType.Dictionary; diff --git a/src/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs b/src/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs index dd4f8ecbb5d3..d423abb96a1e 100644 --- a/src/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs +++ b/src/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs @@ -75,25 +75,58 @@ public static void JsonIgnoreAttribute() Assert.Equal(@"MyString", obj.MyString); Assert.Equal(@"MyStringWithIgnore", obj.MyStringWithIgnore); Assert.Equal(2, obj.MyStringsWithIgnore.Length); + Assert.Equal(@"MyComplexPropertyWithIgnore", obj.MyComplexPropertyWithIgnore.MyProperty); // Verify serialize. string json = JsonSerializer.ToString(obj); Assert.Contains(@"""MyString""", json); Assert.DoesNotContain(@"MyStringWithIgnore", json); Assert.DoesNotContain(@"MyStringsWithIgnore", json); + Assert.DoesNotContain(@"MyComplexPropertyWithIgnore", json); // Verify deserialize default. obj = JsonSerializer.Parse(@"{}"); Assert.Equal(@"MyString", obj.MyString); Assert.Equal(@"MyStringWithIgnore", obj.MyStringWithIgnore); Assert.Equal(2, obj.MyStringsWithIgnore.Length); + Assert.Equal(@"MyComplexPropertyWithIgnore", obj.MyComplexPropertyWithIgnore.MyProperty); - // Verify deserialize ignores the json for MyStringWithIgnore and MyStringsWithIgnore. + // Verify deserialize ignores the json for MyStringWithIgnore, MyStringsWithIgnore and MyComplexPropertyWithIgnore. obj = JsonSerializer.Parse( - @"{""MyString"":""Hello"", ""MyStringWithIgnore"":""IgnoreMe"", ""MyStringsWithIgnore"":[""IgnoreMe""]}"); + @"{""MyString"":""Hello"", ""MyStringWithIgnore"":""IgnoreMe"", ""MyStringsWithIgnore"":[""IgnoreMe""], ""MyComplexPropertyWithIgnore"":{""MyProperty"":""IgnoreMe""}}"); Assert.Contains(@"Hello", obj.MyString); Assert.Equal(@"MyStringWithIgnore", obj.MyStringWithIgnore); Assert.Equal(2, obj.MyStringsWithIgnore.Length); + Assert.Equal(@"MyComplexPropertyWithIgnore", obj.MyComplexPropertyWithIgnore.MyProperty); + } + + [Fact] + public static void JsonIgnoreAttribute_NestedObject() + { + // Verify default state. + var obj = new NestedObjectWithIgnoreAttributeProperty(); + Assert.Equal(@"MyString", obj.MyString); + Assert.Equal(@"MyStringNested", obj.NestedObject.MyStringNested); + Assert.Equal(@"MyStringNested2", obj.NestedObject.NestedObject2.MyStringNested2); + + // Verify serialize. + string json = JsonSerializer.ToString(obj); + Assert.Contains(@"""MyString""", json); + Assert.Contains(@"""MyStringNested""", json); + Assert.DoesNotContain(@"MyStringNested2", json); + + // Verify deserialize default. + obj = JsonSerializer.Parse(@"{}"); + Assert.Equal(@"MyString", obj.MyString); + Assert.Equal(@"MyStringNested", obj.NestedObject.MyStringNested); + Assert.Equal(@"MyStringNested2", obj.NestedObject.NestedObject2.MyStringNested2); + + // Verify deserialize ignores the json for NestedObject2. + obj = JsonSerializer.Parse( + @"{""MyString"":""Hello"",""NestedObject"":{""MyStringNested"":""HelloMyStringNested"",""NestedObject2"":{""MyStringNested2"":""IgnoreMe""}}}"); + Assert.Contains(@"Hello", obj.MyString); + Assert.Contains(@"HelloMyStringNested", obj.NestedObject.MyStringNested); + Assert.Null(obj.NestedObject.NestedObject2); } // Todo: add tests with missing object property and missing collection property. @@ -178,6 +211,7 @@ public ClassWithIgnoreAttributeProperty() MyString = "MyString"; MyStringWithIgnore = "MyStringWithIgnore"; MyStringsWithIgnore = new string[] { "1", "2" }; + MyComplexPropertyWithIgnore = new ComplexType() { MyProperty = "MyComplexPropertyWithIgnore" }; } [JsonIgnore] @@ -187,6 +221,47 @@ public ClassWithIgnoreAttributeProperty() [JsonIgnore] public string[] MyStringsWithIgnore { get; set; } + + [JsonIgnore] + public ComplexType MyComplexPropertyWithIgnore { get; set; } + } + + public class ComplexType + { + public string MyProperty { get; set; } + } + + public class NestedObjectWithIgnoreAttributeProperty + { + public NestedObjectWithIgnoreAttributeProperty() + { + MyString = "MyString"; + NestedObject = new NestedObject() + { + MyStringNested = "MyStringNested", + NestedObject2 = new NestedObject2() + { + MyStringNested2 = "MyStringNested2" + } + }; + } + + public string MyString { get; set; } + + public NestedObject NestedObject { get; set; } + } + + public class NestedObject + { + public string MyStringNested { get; set; } + + [JsonIgnore] + public NestedObject2 NestedObject2 { get; set; } + } + + public class NestedObject2 + { + public string MyStringNested2 { get; set; } } } } From c09986bcb27576828d612a092095816a0940033a Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Fri, 10 May 2019 20:31:37 +0200 Subject: [PATCH 2/3] update test --- .../tests/Serialization/PropertyVisibilityTests.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs b/src/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs index d423abb96a1e..b18403498e25 100644 --- a/src/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs +++ b/src/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs @@ -17,10 +17,14 @@ public static void NoSetter() string json = JsonSerializer.ToString(obj); Assert.Contains(@"""MyString"":""DefaultValue""", json); Assert.Contains(@"""MyInts"":[1,2]", json); + Assert.Contains(@"""MyProperty"":""MyComplexProperty""", json); + Assert.Contains(@"""MyInt"":42", json); - obj = JsonSerializer.Parse(@"{""MyString"":""IgnoreMe"",""MyInts"":[0]}"); + obj = JsonSerializer.Parse(@"{""MyInt"": ""0"",""MyString"":""IgnoreMe"",""MyInts"":[0],""MyComplexProperty"":{""MyProperty"":""IgnoreMe""}}"); Assert.Equal("DefaultValue", obj.MyString); Assert.Equal(2, obj.MyInts.Length); + Assert.Equal("MyComplexProperty", obj.MyComplexProperty.MyProperty); + Assert.Equal(42, obj.MyInt); } [Fact] @@ -150,12 +154,16 @@ public class ClassWithNoSetter { public ClassWithNoSetter() { + MyInt = 42; MyString = "DefaultValue"; MyInts = new int[] { 1, 2 }; + MyComplexProperty = new ComplexType() { MyProperty = "MyComplexProperty" }; } + public int MyInt { get; } public string MyString { get; } public int[] MyInts { get; } + public ComplexType MyComplexProperty { get; } } public class ClassWithNoGetter From 62b42fca3592c1ea2a0e8213835958ea9ce9d66a Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Fri, 10 May 2019 20:45:10 +0200 Subject: [PATCH 3/3] fix HandleNull for !ShouldDeserialize --- .../Json/Serialization/JsonSerializer.Read.HandleNull.cs | 6 ++++++ .../tests/Serialization/PropertyVisibilityTests.cs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleNull.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleNull.cs index 80abfc4a631c..2dc3d293a5fb 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleNull.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleNull.cs @@ -25,6 +25,12 @@ private static bool HandleNull(ref Utf8JsonReader reader, ref ReadStack state, J } JsonPropertyInfo propertyInfo = state.Current.JsonPropertyInfo; + + if (!propertyInfo.ShouldDeserialize) + { + return false; + } + if (!propertyInfo.CanBeNull) { ThrowHelper.ThrowJsonException_DeserializeCannotBeNull(reader, state.PropertyPath); diff --git a/src/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs b/src/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs index b18403498e25..7f47762ee5c1 100644 --- a/src/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs +++ b/src/System.Text.Json/tests/Serialization/PropertyVisibilityTests.cs @@ -25,6 +25,12 @@ public static void NoSetter() Assert.Equal(2, obj.MyInts.Length); Assert.Equal("MyComplexProperty", obj.MyComplexProperty.MyProperty); Assert.Equal(42, obj.MyInt); + + obj = JsonSerializer.Parse(@"{""MyInt"": null,""MyString"":""IgnoreMe"",""MyInts"":[0],""MyComplexProperty"":{""MyProperty"":""IgnoreMe""}}"); + Assert.Equal("DefaultValue", obj.MyString); + Assert.Equal(2, obj.MyInts.Length); + Assert.Equal("MyComplexProperty", obj.MyComplexProperty.MyProperty); + Assert.Equal(42, obj.MyInt); } [Fact]