diff --git a/Src/Newtonsoft.Json.Tests/Serialization/MissingMemberHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/MissingMemberHandlingTests.cs index 23748d85..470ec813 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/MissingMemberHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/MissingMemberHandlingTests.cs @@ -27,6 +27,7 @@ using System; using System.Collections.Generic; using System.IO; using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; using Newtonsoft.Json.Tests.TestObjects; #if DNXCORE50 using Xunit; @@ -154,6 +155,20 @@ namespace Newtonsoft.Json.Tests.Serialization }); } + [Test] + public void MissingErrorAttribute() + { + string json = @"{""Missing"":1}"; + + ExceptionAssert.Throws(() => { JsonConvert.DeserializeObject(json); }, "Could not find member 'Missing' on object of type 'NameWithMissingError'. Path 'Missing', line 1, position 11."); + } + + [JsonObject(MissingMemberHandling = MissingMemberHandling.Error)] + public class NameWithMissingError + { + public string First { get; set; } + } + public class Name { public string First { get; set; } @@ -215,5 +230,46 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(1, errors.Count); Assert.AreEqual("Could not find member 'firstERROR' on object of type 'Name'. Path 'name.firstERROR', line 1, position 20.", errors[0]); } + + [JsonObject(MissingMemberHandling = MissingMemberHandling.Ignore)] + public class SimpleExtendableObject + { + [JsonExtensionData] + public IDictionary Data { get; } = new Dictionary(); + } + + public class ObjectWithExtendableChild + { + public SimpleExtendableObject Data; + } + + [Test] + public void TestMissingMemberHandlingForDirectObjects() + { + string json = @"{""extensionData1"": [1,2,3]}"; + SimpleExtendableObject e2 = JsonConvert.DeserializeObject(json, new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Error }); + JArray o1 = (JArray)e2.Data["extensionData1"]; + Assert.AreEqual(JTokenType.Array, o1.Type); + } + + [Test] + public void TestMissingMemberHandlingForChildObjects() + { + string json = @"{""Data"":{""extensionData1"": [1,2,3]}}"; + ObjectWithExtendableChild e3 = JsonConvert.DeserializeObject(json, new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Error }); + JArray o1 = (JArray)e3.Data.Data["extensionData1"]; + Assert.AreEqual(JTokenType.Array, o1.Type); + } + + [Test] + public void TestMissingMemberHandlingForChildObjectsWithInvalidData() + { + string json = @"{""InvalidData"":{""extensionData1"": [1,2,3]}}"; + + ExceptionAssert.Throws(() => + { + JsonConvert.DeserializeObject(json, new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Error }); + }, "Could not find member 'InvalidData' on object of type 'ObjectWithExtendableChild'. Path 'InvalidData', line 1, position 15."); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonObjectAttribute.cs b/Src/Newtonsoft.Json/JsonObjectAttribute.cs index a5c35e42..4ad8a7af 100644 --- a/Src/Newtonsoft.Json/JsonObjectAttribute.cs +++ b/Src/Newtonsoft.Json/JsonObjectAttribute.cs @@ -34,7 +34,8 @@ namespace Newtonsoft.Json public sealed class JsonObjectAttribute : JsonContainerAttribute { private MemberSerialization _memberSerialization = MemberSerialization.OptOut; - + internal MissingMemberHandling? _missingMemberHandling; + // 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; @@ -50,6 +51,16 @@ namespace Newtonsoft.Json set => _memberSerialization = value; } + /// + /// Gets or sets the missing member handling used when deserializing this object. + /// + /// The missing member handling. + public MissingMemberHandling MissingMemberHandling + { + get => _missingMemberHandling ?? default; + set => _missingMemberHandling = value; + } + /// /// Gets or sets how the object's properties with null values are handled during serialization and deserialization. /// diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index adb29472..a3b934b8 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -356,6 +356,7 @@ namespace Newtonsoft.Json.Serialization { contract.ItemRequired = attribute._itemRequired; contract.ItemNullValueHandling = attribute._itemNullValueHandling; + contract.MissingMemberHandling = attribute._missingMemberHandling; if (attribute.NamingStrategyType != null) { diff --git a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs index aeb5410e..0a839a85 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs @@ -44,6 +44,12 @@ namespace Newtonsoft.Json.Serialization /// The member object serialization. public MemberSerialization MemberSerialization { get; set; } + /// + /// Gets or sets the missing member handling used when deserializing this object. + /// + /// The missing member handling. + public MissingMemberHandling? MissingMemberHandling { get; set; } + /// /// Gets or sets a value that indicates whether the object's properties are required. /// diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 6ad8abee..d0ee4d52 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -2223,7 +2223,7 @@ namespace Newtonsoft.Json.Serialization TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Could not find member '{0}' on {1}.".FormatWith(CultureInfo.InvariantCulture, memberName, contract.UnderlyingType)), null); } - if (Serializer._missingMemberHandling == MissingMemberHandling.Error) + if ((contract.MissingMemberHandling ?? Serializer._missingMemberHandling) == MissingMemberHandling.Error) { throw JsonSerializationException.Create(reader, "Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, objectType.Name)); } @@ -2342,7 +2342,7 @@ namespace Newtonsoft.Json.Serialization TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Could not find member '{0}' on {1}".FormatWith(CultureInfo.InvariantCulture, propertyName, contract.UnderlyingType)), null); } - if (Serializer._missingMemberHandling == MissingMemberHandling.Error) + if ((contract.MissingMemberHandling ?? Serializer._missingMemberHandling) == MissingMemberHandling.Error) { throw JsonSerializationException.Create(reader, "Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, propertyName, contract.UnderlyingType.Name)); }