From a913a72024005ca6c3056e2df3a2b18f268667ed Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 22 Sep 2018 12:34:47 -0700 Subject: [PATCH] Fix blank extension data values with required properties and ctor (#1850) --- Src/Newtonsoft.Json.Tests/Issues/Issue1834.cs | 107 ++++++++++++++++++ Src/Newtonsoft.Json/JsonConvert.cs | 19 ++++ Src/Newtonsoft.Json/JsonSerializer.cs | 6 + .../JsonSerializerInternalReader.cs | 17 +-- 4 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 Src/Newtonsoft.Json.Tests/Issues/Issue1834.cs diff --git a/Src/Newtonsoft.Json.Tests/Issues/Issue1834.cs b/Src/Newtonsoft.Json.Tests/Issues/Issue1834.cs new file mode 100644 index 00000000..46ccea4a --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Issues/Issue1834.cs @@ -0,0 +1,107 @@ +#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 + +#if !NET20 +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization; +#if !(NET20 || NET35 || NET40 || PORTABLE40) +using System.Threading.Tasks; +#endif +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Utilities; +#if DNXCORE50 +using Xunit; +using Test = Xunit.FactAttribute; +using Assert = Newtonsoft.Json.Tests.XUnitAssert; +#else +using NUnit.Framework; +#endif + +namespace Newtonsoft.Json.Tests.Issues +{ + [TestFixture] + public class Issue1834 : TestFixtureBase + { + [Test] + public void Test() + { + string json = "{'foo':'test!'}"; + ItemWithJsonConstructor c = JsonConvert.DeserializeObject(json); + + Assert.IsNull(c.ExtensionData); + } + + [Test] + public void Test_UnsetRequired() + { + string json = "{'foo':'test!'}"; + ItemWithJsonConstructorAndDefaultValue c = JsonConvert.DeserializeObject(json); + + Assert.IsNull(c.ExtensionData); + } + + public class ItemWithJsonConstructor + { + [JsonExtensionData] + public IDictionary ExtensionData; + + [JsonConstructor] + private ItemWithJsonConstructor(string foo) + { + Foo = foo; + } + + [JsonProperty(PropertyName = "foo", Required = Required.Always)] + public string Foo { get; set; } + } + + public class ItemWithJsonConstructorAndDefaultValue + { + [JsonExtensionData] + public IDictionary ExtensionData; + + [JsonConstructor] + private ItemWithJsonConstructorAndDefaultValue(string foo) + { + Foo = foo; + } + + [JsonProperty("foo")] + public string Foo { get; set; } + + [JsonProperty(PropertyName = "bar", Required = Required.Default, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [System.ComponentModel.DefaultValue("default")] + public string Bar { get; set; } + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index 608bf0db..2e5935d7 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -35,6 +35,7 @@ using System.Xml; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using System.Text; +using System.Diagnostics; #if HAVE_XLINQ using System.Xml.Linq; @@ -522,6 +523,7 @@ namespace Newtonsoft.Json /// /// The object to serialize. /// A JSON string representation of the object. + [DebuggerStepThrough] public static string SerializeObject(object value) { return SerializeObject(value, null, (JsonSerializerSettings)null); @@ -535,6 +537,7 @@ namespace Newtonsoft.Json /// /// A JSON string representation of the object. /// + [DebuggerStepThrough] public static string SerializeObject(object value, Formatting formatting) { return SerializeObject(value, formatting, (JsonSerializerSettings)null); @@ -546,6 +549,7 @@ namespace Newtonsoft.Json /// The object to serialize. /// A collection of converters used while serializing. /// A JSON string representation of the object. + [DebuggerStepThrough] public static string SerializeObject(object value, params JsonConverter[] converters) { JsonSerializerSettings settings = (converters != null && converters.Length > 0) @@ -562,6 +566,7 @@ namespace Newtonsoft.Json /// Indicates how the output should be formatted. /// A collection of converters used while serializing. /// A JSON string representation of the object. + [DebuggerStepThrough] public static string SerializeObject(object value, Formatting formatting, params JsonConverter[] converters) { JsonSerializerSettings settings = (converters != null && converters.Length > 0) @@ -580,6 +585,7 @@ namespace Newtonsoft.Json /// /// A JSON string representation of the object. /// + [DebuggerStepThrough] public static string SerializeObject(object value, JsonSerializerSettings settings) { return SerializeObject(value, null, settings); @@ -599,6 +605,7 @@ namespace Newtonsoft.Json /// /// A JSON string representation of the object. /// + [DebuggerStepThrough] public static string SerializeObject(object value, Type type, JsonSerializerSettings settings) { JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); @@ -616,6 +623,7 @@ namespace Newtonsoft.Json /// /// A JSON string representation of the object. /// + [DebuggerStepThrough] public static string SerializeObject(object value, Formatting formatting, JsonSerializerSettings settings) { return SerializeObject(value, null, formatting, settings); @@ -636,6 +644,7 @@ namespace Newtonsoft.Json /// /// A JSON string representation of the object. /// + [DebuggerStepThrough] public static string SerializeObject(object value, Type type, Formatting formatting, JsonSerializerSettings settings) { JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); @@ -665,6 +674,7 @@ namespace Newtonsoft.Json /// /// The JSON to deserialize. /// The deserialized object from the JSON string. + [DebuggerStepThrough] public static object DeserializeObject(string value) { return DeserializeObject(value, null, (JsonSerializerSettings)null); @@ -679,6 +689,7 @@ namespace Newtonsoft.Json /// If this is null, default serialization settings will be used. /// /// The deserialized object from the JSON string. + [DebuggerStepThrough] public static object DeserializeObject(string value, JsonSerializerSettings settings) { return DeserializeObject(value, null, settings); @@ -690,6 +701,7 @@ namespace Newtonsoft.Json /// The JSON to deserialize. /// The of object being deserialized. /// The deserialized object from the JSON string. + [DebuggerStepThrough] public static object DeserializeObject(string value, Type type) { return DeserializeObject(value, type, (JsonSerializerSettings)null); @@ -701,6 +713,7 @@ namespace Newtonsoft.Json /// The type of the object to deserialize to. /// The JSON to deserialize. /// The deserialized object from the JSON string. + [DebuggerStepThrough] public static T DeserializeObject(string value) { return DeserializeObject(value, (JsonSerializerSettings)null); @@ -717,6 +730,7 @@ namespace Newtonsoft.Json /// The JSON to deserialize. /// The anonymous type object. /// The deserialized anonymous type from the JSON string. + [DebuggerStepThrough] public static T DeserializeAnonymousType(string value, T anonymousTypeObject) { return DeserializeObject(value); @@ -737,6 +751,7 @@ namespace Newtonsoft.Json /// If this is null, default serialization settings will be used. /// /// The deserialized anonymous type from the JSON string. + [DebuggerStepThrough] public static T DeserializeAnonymousType(string value, T anonymousTypeObject, JsonSerializerSettings settings) { return DeserializeObject(value, settings); @@ -749,6 +764,7 @@ namespace Newtonsoft.Json /// The JSON to deserialize. /// Converters to use while deserializing. /// The deserialized object from the JSON string. + [DebuggerStepThrough] public static T DeserializeObject(string value, params JsonConverter[] converters) { return (T)DeserializeObject(value, typeof(T), converters); @@ -764,6 +780,7 @@ namespace Newtonsoft.Json /// If this is null, default serialization settings will be used. /// /// The deserialized object from the JSON string. + [DebuggerStepThrough] public static T DeserializeObject(string value, JsonSerializerSettings settings) { return (T)DeserializeObject(value, typeof(T), settings); @@ -776,6 +793,7 @@ namespace Newtonsoft.Json /// The type of the object to deserialize. /// Converters to use while deserializing. /// The deserialized object from the JSON string. + [DebuggerStepThrough] public static object DeserializeObject(string value, Type type, params JsonConverter[] converters) { JsonSerializerSettings settings = (converters != null && converters.Length > 0) @@ -820,6 +838,7 @@ namespace Newtonsoft.Json /// /// The JSON to populate values from. /// The target object to populate values onto. + [DebuggerStepThrough] public static void PopulateObject(string value, object target) { PopulateObject(value, target, null); diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index aac6cede..a960b871 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -790,6 +790,7 @@ namespace Newtonsoft.Json /// /// The that contains the JSON structure to read values from. /// The target object to populate values onto. + [DebuggerStepThrough] public void Populate(TextReader reader, object target) { Populate(new JsonTextReader(reader), target); @@ -800,6 +801,7 @@ namespace Newtonsoft.Json /// /// The that contains the JSON structure to read values from. /// The target object to populate values onto. + [DebuggerStepThrough] public void Populate(JsonReader reader, object target) { PopulateInternal(reader, target); @@ -839,6 +841,7 @@ namespace Newtonsoft.Json /// /// The that contains the JSON structure to deserialize. /// The being deserialized. + [DebuggerStepThrough] public object Deserialize(JsonReader reader) { return Deserialize(reader, null); @@ -851,6 +854,7 @@ namespace Newtonsoft.Json /// The containing the object. /// The of object being deserialized. /// The instance of being deserialized. + [DebuggerStepThrough] public object Deserialize(TextReader reader, Type objectType) { return Deserialize(new JsonTextReader(reader), objectType); @@ -863,6 +867,7 @@ namespace Newtonsoft.Json /// The containing the object. /// The type of the object to deserialize. /// The instance of being deserialized. + [DebuggerStepThrough] public T Deserialize(JsonReader reader) { return (T)Deserialize(reader, typeof(T)); @@ -875,6 +880,7 @@ namespace Newtonsoft.Json /// The containing the object. /// The of object being deserialized. /// The instance of being deserialized. + [DebuggerStepThrough] public object Deserialize(JsonReader reader, Type objectType) { return DeserializeInternal(reader, objectType); diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 1e807373..0de92a37 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -1906,14 +1906,17 @@ namespace Newtonsoft.Json.Serialization { foreach (JsonProperty property in contract.Properties) { - if (propertyContexts.All(p => p.Property != property)) + if (!property.Ignored) { - propertyContexts.Add(new CreatorPropertyContext + if (propertyContexts.All(p => p.Property != property)) { - Property = property, - Name = property.PropertyName, - Presence = PropertyPresence.None - }); + propertyContexts.Add(new CreatorPropertyContext + { + Property = property, + Name = property.PropertyName, + Presence = PropertyPresence.None + }); + } } } } @@ -2076,7 +2079,7 @@ namespace Newtonsoft.Json.Serialization { foreach (CreatorPropertyContext propertyValue in propertyContexts) { - if (!propertyValue.Used) + if (!propertyValue.Used && propertyValue.Presence != PropertyPresence.None) { contract.ExtensionDataSetter(createdObject, propertyValue.Name, propertyValue.Value); }