From b3a2ae1946b73ab7d41167f54fab91a2f17cfb19 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Tue, 25 Jun 2019 12:46:27 -0700 Subject: [PATCH] Fixing up HandleNull in JsonSerializer.Read to reset the property for dictionaries. (dotnet/corefx#38704) * Fixing up HandleNull in JsonSerializer.Read to reset the property for dictionaries. * Adding regression tests for dotnet/corefx#38565 * Fixing HandleDictionary in JsonSerializer.Write to respect IgnoreNullValues * Updating the Regression38565_Serialize test to not expect null * Responding to PR feedback and adding a regression test for dotnet/corefx#38557 Commit migrated from https://github.com/dotnet/corefx/commit/78e00d4b0ad5b390c9a54431c632062392c3ed7e --- .../JsonSerializer.Read.HandleNull.cs | 7 + .../JsonSerializer.Write.HandleDictionary.cs | 8 +- .../tests/Serialization/DictionaryTests.cs | 149 +++++++++++++++++++++ 3 files changed, 162 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleNull.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleNull.cs index 70258d9..7fe07d1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleNull.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleNull.cs @@ -39,6 +39,13 @@ namespace System.Text.Json { bool setPropertyToNull = !state.Current.CollectionPropertyInitialized; ApplyObjectToEnumerable(null, ref state, ref reader, setPropertyDirectly: setPropertyToNull); + + if (setPropertyToNull) + { + // The property itself was set to null, in which case we should + // reset so that `Is*Property` no longer returns true + state.Current.ResetProperty(); + } return false; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs index f799734..b40d502 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs @@ -25,8 +25,12 @@ namespace System.Text.Json enumerable = (IEnumerable)jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue); if (enumerable == null) { - // Write a null object or enumerable. - state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer, writeNull: true); + if (!state.Current.JsonPropertyInfo.IgnoreNullValues) + { + // Write a null object or enumerable. + state.Current.WriteObjectOrArrayStart(ClassType.Enumerable, writer, writeNull: true); + } + return true; } diff --git a/src/libraries/System.Text.Json/tests/Serialization/DictionaryTests.cs b/src/libraries/System.Text.Json/tests/Serialization/DictionaryTests.cs index d05438d..2dd2d78 100644 --- a/src/libraries/System.Text.Json/tests/Serialization/DictionaryTests.cs +++ b/src/libraries/System.Text.Json/tests/Serialization/DictionaryTests.cs @@ -905,6 +905,130 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal("1", actual.Child["1"].A); } + [Fact] + public static void Regression38565_Serialize() + { + var value = new Regression38565_Parent() + { + Test = "value1", + Child = new Regression38565_Child() + }; + + var actual = JsonSerializer.ToString(value); + Assert.Equal("{\"Test\":\"value1\",\"Dict\":null,\"Child\":{\"Test\":null,\"Dict\":null}}", actual); + } + + [Fact] + public static void Regression38565_Deserialize() + { + var json = "{\"Test\":\"value1\",\"Dict\":null,\"Child\":{\"Test\":null,\"Dict\":null}}"; + Regression38565_Parent actual = JsonSerializer.Parse(json); + + Assert.Equal("value1", actual.Test); + Assert.Null(actual.Dict); + Assert.NotNull(actual.Child); + Assert.Null(actual.Child.Dict); + Assert.Null(actual.Child.Test); + } + + [Fact] + public static void Regression38565_Serialize_IgnoreNullValues() + { + var value = new Regression38565_Parent() + { + Test = "value1", + Child = new Regression38565_Child() + }; + + var actual = JsonSerializer.ToString(value, new JsonSerializerOptions { IgnoreNullValues = true }); + Assert.Equal("{\"Test\":\"value1\",\"Child\":{}}", actual); + } + + [Fact] + public static void Regression38565_Deserialize_IgnoreNullValues() + { + var json = "{\"Test\":\"value1\",\"Child\":{}}"; + Regression38565_Parent actual = JsonSerializer.Parse(json); + + Assert.Equal("value1", actual.Test); + Assert.Null(actual.Dict); + Assert.NotNull(actual.Child); + Assert.Null(actual.Child.Dict); + Assert.Null(actual.Child.Test); + } + + [Fact] + public static void Regression38557_Serialize() + { + var dictionaryFirst = new Regression38557_DictionaryFirst() + { + Test = "value1" + }; + + var actual = JsonSerializer.ToString(dictionaryFirst); + Assert.Equal("{\"Dict\":null,\"Test\":\"value1\"}", actual); + + var dictionaryLast = new Regression38557_DictionaryLast() + { + Test = "value1" + }; + + actual = JsonSerializer.ToString(dictionaryLast); + Assert.Equal("{\"Test\":\"value1\",\"Dict\":null}", actual); + } + + [Fact] + public static void Regression38557_Deserialize() + { + var json = "{\"Dict\":null,\"Test\":\"value1\"}"; + Regression38557_DictionaryFirst dictionaryFirst = JsonSerializer.Parse(json); + + Assert.Equal("value1", dictionaryFirst.Test); + Assert.Null(dictionaryFirst.Dict); + + json = "{\"Test\":\"value1\",\"Dict\":null}"; + Regression38557_DictionaryLast dictionaryLast = JsonSerializer.Parse(json); + + Assert.Equal("value1", dictionaryLast.Test); + Assert.Null(dictionaryLast.Dict); + } + + [Fact] + public static void Regression38557_Serialize_IgnoreNullValues() + { + var dictionaryFirst = new Regression38557_DictionaryFirst() + { + Test = "value1" + }; + + var actual = JsonSerializer.ToString(dictionaryFirst, new JsonSerializerOptions { IgnoreNullValues = true }); + Assert.Equal("{\"Test\":\"value1\"}", actual); + + var dictionaryLast = new Regression38557_DictionaryLast() + { + Test = "value1" + }; + + actual = JsonSerializer.ToString(dictionaryLast, new JsonSerializerOptions { IgnoreNullValues = true }); + Assert.Equal("{\"Test\":\"value1\"}", actual); + } + + [Fact] + public static void Regression38557_Deserialize_IgnoreNullValues() + { + var json = "{\"Test\":\"value1\"}"; + Regression38557_DictionaryFirst dictionaryFirst = JsonSerializer.Parse(json); + + Assert.Equal("value1", dictionaryFirst.Test); + Assert.Null(dictionaryFirst.Dict); + + json = "{\"Test\":\"value1\"}"; + Regression38557_DictionaryLast dictionaryLast = JsonSerializer.Parse(json); + + Assert.Equal("value1", dictionaryLast.Test); + Assert.Null(dictionaryLast.Dict); + } + public class ClassWithDictionaryButNoSetter { public Dictionary MyDictionary { get; } = new Dictionary(); @@ -939,5 +1063,30 @@ namespace System.Text.Json.Serialization.Tests public int? J { get; set; } public string[] K { get; set; } } + + public class Regression38565_Parent + { + public string Test { get; set; } + public Dictionary Dict { get; set; } + public Regression38565_Child Child { get; set; } + } + + public class Regression38565_Child + { + public string Test { get; set; } + public Dictionary Dict { get; set; } + } + + public class Regression38557_DictionaryLast + { + public string Test { get; set; } + public Dictionary Dict { get; set; } + } + + public class Regression38557_DictionaryFirst + { + public Dictionary Dict { get; set; } + public string Test { get; set; } + } } } -- 2.7.4