diff --git a/Src/Newtonsoft.Json.Tests/Serialization/MissingMemberHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/MissingMemberHandlingTests.cs index c206a697..07c04343 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/MissingMemberHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/MissingMemberHandlingTests.cs @@ -24,6 +24,7 @@ #endregion using System; +using System.Collections.Generic; using System.IO; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Tests.TestObjects; @@ -67,7 +68,10 @@ namespace Newtonsoft.Json.Tests.Serialization // ] //} - ExceptionAssert.Throws(() => { ProductShort deserializedProductShort = (ProductShort)JsonConvert.DeserializeObject(output, typeof(ProductShort), new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Error }); }, @"Could not find member 'Price' on object of type 'ProductShort'. Path 'Price', line 4, position 10."); + ExceptionAssert.Throws(() => + { + ProductShort deserializedProductShort = (ProductShort)JsonConvert.DeserializeObject(output, typeof(ProductShort), new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Error }); + }, @"Could not find member 'Price' on object of type 'ProductShort'. Path 'Price', line 4, position 10."); } [Test] @@ -153,5 +157,67 @@ namespace Newtonsoft.Json.Tests.Serialization MissingMemberHandling = MissingMemberHandling.Error }); } + + public class Name + { + public string First { get; set; } + } + + public class Person + { + public Name Name { get; set; } + } + + [Test] + public void MissingMemberHandling_RootObject() + { + IList errors = new List(); + + JsonSerializerSettings settings = new JsonSerializerSettings + { + //This works on properties but not on a objects property. + /* So nameERROR:{"first":"ni"} would throw. The payload name:{"firstERROR":"hi"} would not */ + MissingMemberHandling = MissingMemberHandling.Error, + Error = (sender, args) => + { + // A more concrete error type would be nice but we are limited by Newtonsofts library here. + errors.Add(args.ErrorContext.Error.Message); + args.ErrorContext.Handled = true; + } + }; + + Person p = new Person(); + + JsonConvert.PopulateObject(@"{nameERROR:{""first"":""hi""}}", p, settings); + + Assert.AreEqual(1, errors.Count); + Assert.AreEqual("Could not find member 'nameERROR' on object of type 'Person'. Path 'nameERROR', line 1, position 11.", errors[0]); + } + + [Test] + public void MissingMemberHandling_InnerObject() + { + IList errors = new List(); + + JsonSerializerSettings settings = new JsonSerializerSettings + { + //This works on properties but not on a objects property. + /* So nameERROR:{"first":"ni"} would throw. The payload name:{"firstERROR":"hi"} would not */ + MissingMemberHandling = MissingMemberHandling.Error, + Error = (sender, args) => + { + // A more concrete error type would be nice but we are limited by Newtonsofts library here. + errors.Add(args.ErrorContext.Error.Message); + args.ErrorContext.Handled = true; + } + }; + + Person p = new Person(); + + JsonConvert.PopulateObject(@"{name:{""firstERROR"":""hi""}}", p, settings); + + 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]); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs index 1491c0ac..8d1df730 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs @@ -921,7 +921,15 @@ namespace Newtonsoft.Json.Tests.Serialization { IList errorMessages = new List(); - JsonReader reader = new JsonTextReader(new StringReader("{\"string1\":\"blah\",\"int1\":2147483648,\"string2\":\"also blah\",\"int2\":2147483648,\"string3\":\"more blah\"}")); + JsonReader reader = new JsonTextReader(new StringReader(@"{ + ""string1"": ""blah"", + ""int1"": 2147483648, + ""string2"": ""also blah"", + ""int2"": 2147483648, + ""string3"": ""more blah"", + ""dateTime1"": ""200NOTDATE"", + ""string4"": ""even more blah"" +}")); JsonSerializerSettings settings = new JsonSerializerSettings(); settings.Error = (sender, args) => { @@ -938,10 +946,13 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual("also blah", data.String2); Assert.AreEqual(0, data.Int2); Assert.AreEqual("more blah", data.String3); + Assert.AreEqual(default(DateTime), data.DateTime1); + Assert.AreEqual("even more blah", data.String4); - Assert.AreEqual(2, errorMessages.Count); - Assert.AreEqual("JSON integer 2147483648 is too large or small for an Int32. Path 'int1', line 1, position 35.", errorMessages[0]); - Assert.AreEqual("JSON integer 2147483648 is too large or small for an Int32. Path 'int2', line 1, position 75.", errorMessages[1]); + //Assert.AreEqual(2, errorMessages.Count); + Assert.AreEqual("JSON integer 2147483648 is too large or small for an Int32. Path 'int1', line 3, position 20.", errorMessages[0]); + Assert.AreEqual("JSON integer 2147483648 is too large or small for an Int32. Path 'int2', line 5, position 20.", errorMessages[1]); + Assert.AreEqual("Could not convert string to DateTime: 200NOTDATE. Path 'dateTime1', line 7, position 27.", errorMessages[2]); } private class DataModel @@ -951,6 +962,8 @@ namespace Newtonsoft.Json.Tests.Serialization public string String2 { get; set; } public int Int2 { get; set; } public string String3 { get; set; } + public DateTime DateTime1 { get; set; } + public string String4 { get; set; } } } diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs index 7384fbbc..cb39b6eb 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -1851,7 +1851,7 @@ namespace Newtonsoft.Json } catch (Exception ex) { - throw JsonReaderException.Create(this, "Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number), ex); + throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number), ex); } } else @@ -1859,7 +1859,7 @@ namespace Newtonsoft.Json double value; if (!double.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out value)) { - throw JsonReaderException.Create(this, "Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); + throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); } } @@ -1885,7 +1885,7 @@ namespace Newtonsoft.Json } catch (Exception ex) { - throw JsonReaderException.Create(this, "Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, number), ex); + throw ThrowReaderError("Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, number), ex); } } else @@ -1898,11 +1898,11 @@ namespace Newtonsoft.Json } else if (parseResult == ParseResult.Overflow) { - throw JsonReaderException.Create(this, "JSON integer {0} is too large or small for an Int32.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); + throw ThrowReaderError("JSON integer {0} is too large or small for an Int32.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); } else { - throw JsonReaderException.Create(this, "Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); + throw ThrowReaderError("Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); } } @@ -1928,7 +1928,7 @@ namespace Newtonsoft.Json } catch (Exception ex) { - throw JsonReaderException.Create(this, "Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, number), ex); + throw ThrowReaderError("Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, number), ex); } } else @@ -1942,7 +1942,7 @@ namespace Newtonsoft.Json } else { - throw JsonReaderException.Create(this, "Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); + throw ThrowReaderError("Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); } } @@ -1968,7 +1968,7 @@ namespace Newtonsoft.Json } catch (Exception ex) { - throw JsonReaderException.Create(this, "Input string '{0}' is not a valid double.".FormatWith(CultureInfo.InvariantCulture, number), ex); + throw ThrowReaderError("Input string '{0}' is not a valid double.".FormatWith(CultureInfo.InvariantCulture, number), ex); } } else @@ -1981,7 +1981,7 @@ namespace Newtonsoft.Json } else { - throw JsonReaderException.Create(this, "Input string '{0}' is not a valid double.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); + throw ThrowReaderError("Input string '{0}' is not a valid double.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); } } @@ -2005,7 +2005,7 @@ namespace Newtonsoft.Json } catch (Exception ex) { - throw JsonReaderException.Create(this, "Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number), ex); + throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number), ex); } numberType = JsonToken.Integer; @@ -2026,13 +2026,13 @@ namespace Newtonsoft.Json if (number.Length > MaximumJavascriptIntegerCharacterLength) { - throw JsonReaderException.Create(this, "JSON integer {0} is too large to parse.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); + throw ThrowReaderError("JSON integer {0} is too large to parse.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); } numberValue = BigIntegerParse(number, CultureInfo.InvariantCulture); numberType = JsonToken.Integer; #else - throw JsonReaderException.Create(this, "JSON integer {0} is too large or small for an Int64.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); + throw ThrowReaderError("JSON integer {0} is too large or small for an Int64.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); #endif } else @@ -2048,7 +2048,7 @@ namespace Newtonsoft.Json } else { - throw JsonReaderException.Create(this, "Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, number)); + throw ThrowReaderError("Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, number)); } } else @@ -2061,7 +2061,7 @@ namespace Newtonsoft.Json } else { - throw JsonReaderException.Create(this, "Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number)); + throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number)); } } @@ -2076,6 +2076,12 @@ namespace Newtonsoft.Json SetToken(numberType, numberValue, false); } + private JsonReaderException ThrowReaderError(string message, Exception ex = null) + { + SetToken(JsonToken.Undefined, null, false); + return JsonReaderException.Create(this, message, ex); + } + #if !(NET20 || NET35 || PORTABLE40 || PORTABLE) || NETSTANDARD1_1 // By using the BigInteger type in a separate method, // the runtime can execute the ParseNumber even if diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index e04288b1..653810b3 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -2382,7 +2382,7 @@ namespace Newtonsoft.Json.Serialization { if (IsErrorHandled(newObject, contract, memberName, reader as IJsonLineInfo, reader.Path, ex)) { - HandleError(reader, false, initialDepth); + HandleError(reader, true, initialDepth); } else {