From 7d44b61c32e51305e2b445c54fceec69827c4a18 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Thu, 2 Nov 2017 10:55:08 -0400 Subject: [PATCH 1/3] Resolve #1429 for JsonObjectAttribute only: add NullValueHandling property --- .../Serialization/NullValueHandlingTests.cs | 74 ++++++++++++++++--- ...ieWithJsonObjectNullValueHandlingIgnore.cs | 41 ++++++++++ ...eWithJsonObjectNullValueHandlingInclude.cs | 41 ++++++++++ Src/Newtonsoft.Json/JsonObjectAttribute.cs | 11 +++ .../Serialization/DefaultContractResolver.cs | 5 ++ 5 files changed, 160 insertions(+), 12 deletions(-) create mode 100644 Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingIgnore.cs create mode 100644 Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingInclude.cs diff --git a/Src/Newtonsoft.Json.Tests/Serialization/NullValueHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/NullValueHandlingTests.cs index 18eb4ea4..d1e884b3 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/NullValueHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/NullValueHandlingTests.cs @@ -40,6 +40,20 @@ namespace Newtonsoft.Json.Tests.Serialization [TestFixture] public class NullValueHandlingTests : TestFixtureBase { + private const string MovieNullValueHandlingIncludeExpectedResult = @"{ + ""Name"": ""Bad Boys III"", + ""Description"": ""It's no Bad Boys"", + ""Classification"": null, + ""Studio"": null, + ""ReleaseDate"": null, + ""ReleaseCountries"": null +}"; + + private const string MovieNullValueHandlingIgnoreExpectedResult = @"{ + ""Name"": ""Bad Boys III"", + ""Description"": ""It's no Bad Boys"" +}"; + #if !NET20 [Test] public void DeserializeNullIntoDateTime() @@ -110,19 +124,55 @@ namespace Newtonsoft.Json.Tests.Serialization // "Description": "It's no Bad Boys" // } - StringAssert.AreEqual(@"{ - ""Name"": ""Bad Boys III"", - ""Description"": ""It's no Bad Boys"", - ""Classification"": null, - ""Studio"": null, - ""ReleaseDate"": null, - ""ReleaseCountries"": null -}", included); + StringAssert.AreEqual(MovieNullValueHandlingIncludeExpectedResult, included); - StringAssert.AreEqual(@"{ - ""Name"": ""Bad Boys III"", - ""Description"": ""It's no Bad Boys"" -}", ignored); + StringAssert.AreEqual(MovieNullValueHandlingIgnoreExpectedResult, ignored); + } + + [Test] + public void JsonObjectNullValueHandlingIgnore() + { + var movie = new MovieWithJsonObjectNullValueHandlingIgnore + { + Name = "Bad Boys III", + Description = "It's no Bad Boys" + }; + + string ignored = JsonConvert.SerializeObject(movie, + Formatting.Indented, + new JsonSerializerSettings { NullValueHandling = NullValueHandling.Include }); + + // { + // "Name": "Bad Boys III", + // "Description": "It's no Bad Boys" + // } + + StringAssert.AreEqual(MovieNullValueHandlingIgnoreExpectedResult, ignored); + } + + [Test] + public void JsonObjectNullValueHandlingInclude() + { + var movie = new MovieWithJsonObjectNullValueHandlingInclude + { + Name = "Bad Boys III", + Description = "It's no Bad Boys" + }; + + string included = JsonConvert.SerializeObject(movie, + Formatting.Indented, + new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + + // { + // "Name": "Bad Boys III", + // "Description": "It's no Bad Boys", + // "Classification": null, + // "Studio": null, + // "ReleaseDate": null, + // "ReleaseCountries": null + // } + + StringAssert.AreEqual(MovieNullValueHandlingIncludeExpectedResult, included); } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingIgnore.cs b/Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingIgnore.cs new file mode 100644 index 00000000..fd9d9860 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingIgnore.cs @@ -0,0 +1,41 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + [JsonObject(NullValueHandling = NullValueHandling.Ignore)] + public class MovieWithJsonObjectNullValueHandlingIgnore + { + public string Name { get; set; } + public string Description { get; set; } + public string Classification { get; set; } + public string Studio { get; set; } + public DateTime? ReleaseDate { get; set; } + public List ReleaseCountries { get; set; } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingInclude.cs b/Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingInclude.cs new file mode 100644 index 00000000..a89d09f6 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingInclude.cs @@ -0,0 +1,41 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + [JsonObject(NullValueHandling = NullValueHandling.Include)] + public class MovieWithJsonObjectNullValueHandlingInclude + { + public string Name { get; set; } + public string Description { get; set; } + public string Classification { get; set; } + public string Studio { get; set; } + public DateTime? ReleaseDate { get; set; } + public List ReleaseCountries { get; set; } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonObjectAttribute.cs b/Src/Newtonsoft.Json/JsonObjectAttribute.cs index 5f824993..bb3269a5 100644 --- a/Src/Newtonsoft.Json/JsonObjectAttribute.cs +++ b/Src/Newtonsoft.Json/JsonObjectAttribute.cs @@ -34,6 +34,7 @@ namespace Newtonsoft.Json public sealed class JsonObjectAttribute : JsonContainerAttribute { private MemberSerialization _memberSerialization = MemberSerialization.OptOut; + private NullValueHandling _nullValueHandling = NullValueHandling.Include; // yuck. can't set nullable properties on an attribute in C# // have to use this approach to get an unset default state @@ -49,6 +50,16 @@ namespace Newtonsoft.Json set => _memberSerialization = value; } + /// + /// Gets or sets the null value handling. + /// + /// The null value handling. + public NullValueHandling NullValueHandling + { + get => _nullValueHandling; + set => _nullValueHandling = value; + } + /// /// Gets or sets a value that indicates whether the object's properties are required. /// diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 053468a2..d69aa1cb 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -1509,6 +1509,11 @@ namespace Newtonsoft.Json.Serialization #endif } + if (property.NullValueHandling == null && containerAttribute is JsonObjectAttribute objectAttribute) + { + property.NullValueHandling = objectAttribute.NullValueHandling; + } + if (requiredAttribute != null) { property._required = Required.Always; From 61291245076aa6b85ac774326a95fb9f970058a7 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Thu, 2 Nov 2017 11:01:34 -0400 Subject: [PATCH 2/3] For #1429, respect unset initial value on JsonObjectAttribute --- Src/Newtonsoft.Json/JsonObjectAttribute.cs | 6 +++--- .../Serialization/DefaultContractResolver.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Src/Newtonsoft.Json/JsonObjectAttribute.cs b/Src/Newtonsoft.Json/JsonObjectAttribute.cs index bb3269a5..94d4aa6c 100644 --- a/Src/Newtonsoft.Json/JsonObjectAttribute.cs +++ b/Src/Newtonsoft.Json/JsonObjectAttribute.cs @@ -34,11 +34,11 @@ namespace Newtonsoft.Json public sealed class JsonObjectAttribute : JsonContainerAttribute { private MemberSerialization _memberSerialization = MemberSerialization.OptOut; - private NullValueHandling _nullValueHandling = NullValueHandling.Include; - + // yuck. can't set nullable properties on an attribute in C# // have to use this approach to get an unset default state internal Required? _itemRequired; + internal NullValueHandling? _nullValueHandling; /// /// Gets or sets the member serialization. @@ -56,7 +56,7 @@ namespace Newtonsoft.Json /// The null value handling. public NullValueHandling NullValueHandling { - get => _nullValueHandling; + get => _nullValueHandling ?? default(NullValueHandling); set => _nullValueHandling = value; } diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index d69aa1cb..4911de64 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -1511,7 +1511,7 @@ namespace Newtonsoft.Json.Serialization if (property.NullValueHandling == null && containerAttribute is JsonObjectAttribute objectAttribute) { - property.NullValueHandling = objectAttribute.NullValueHandling; + property.NullValueHandling = objectAttribute._nullValueHandling; } if (requiredAttribute != null) From 28405e7309c17d4acd80cd3f37c4368d9e369912 Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Thu, 2 Nov 2017 11:22:27 -0400 Subject: [PATCH 3/3] Per discussion on #1429, rename NullValueHandling to ItemNullValueHandling --- .../MovieWithJsonObjectNullValueHandlingIgnore.cs | 2 +- .../MovieWithJsonObjectNullValueHandlingInclude.cs | 2 +- Src/Newtonsoft.Json/JsonObjectAttribute.cs | 8 ++++---- .../Serialization/DefaultContractResolver.cs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingIgnore.cs b/Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingIgnore.cs index fd9d9860..6efc2ebc 100644 --- a/Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingIgnore.cs +++ b/Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingIgnore.cs @@ -28,7 +28,7 @@ using System.Collections.Generic; namespace Newtonsoft.Json.Tests.TestObjects { - [JsonObject(NullValueHandling = NullValueHandling.Ignore)] + [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] public class MovieWithJsonObjectNullValueHandlingIgnore { public string Name { get; set; } diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingInclude.cs b/Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingInclude.cs index a89d09f6..1a1acdb6 100644 --- a/Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingInclude.cs +++ b/Src/Newtonsoft.Json.Tests/TestObjects/MovieWithJsonObjectNullValueHandlingInclude.cs @@ -28,7 +28,7 @@ using System.Collections.Generic; namespace Newtonsoft.Json.Tests.TestObjects { - [JsonObject(NullValueHandling = NullValueHandling.Include)] + [JsonObject(ItemNullValueHandling = NullValueHandling.Include)] public class MovieWithJsonObjectNullValueHandlingInclude { public string Name { get; set; } diff --git a/Src/Newtonsoft.Json/JsonObjectAttribute.cs b/Src/Newtonsoft.Json/JsonObjectAttribute.cs index 94d4aa6c..0b532f29 100644 --- a/Src/Newtonsoft.Json/JsonObjectAttribute.cs +++ b/Src/Newtonsoft.Json/JsonObjectAttribute.cs @@ -38,7 +38,7 @@ namespace Newtonsoft.Json // yuck. can't set nullable properties on an attribute in C# // have to use this approach to get an unset default state internal Required? _itemRequired; - internal NullValueHandling? _nullValueHandling; + internal NullValueHandling? _itemNullValueHandling; /// /// Gets or sets the member serialization. @@ -54,10 +54,10 @@ namespace Newtonsoft.Json /// Gets or sets the null value handling. /// /// The null value handling. - public NullValueHandling NullValueHandling + public NullValueHandling ItemNullValueHandling { - get => _nullValueHandling ?? default(NullValueHandling); - set => _nullValueHandling = value; + get => _itemNullValueHandling ?? default(NullValueHandling); + set => _itemNullValueHandling = value; } /// diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 4911de64..d5a75857 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -1511,7 +1511,7 @@ namespace Newtonsoft.Json.Serialization if (property.NullValueHandling == null && containerAttribute is JsonObjectAttribute objectAttribute) { - property.NullValueHandling = objectAttribute._nullValueHandling; + property.NullValueHandling = objectAttribute._itemNullValueHandling; } if (requiredAttribute != null)