diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 7b608449..6aa07234 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -72,5 +72,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.5.8.15201")] +[assembly: AssemblyFileVersion("4.5.8.15204")] #endif diff --git a/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs index e99f6511..0ea50f0a 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs @@ -181,7 +181,7 @@ namespace Newtonsoft.Json.Tests.Serialization [Test] public void DynamicCamelCasePropertyNames() { - dynamic o = new DynamicTests.TestDynamicObject(); + dynamic o = new TestDynamicObject(); o.Text = "Text!"; o.Integer = int.MaxValue; diff --git a/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs index bd0d466b..e2675f2a 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs @@ -50,77 +50,6 @@ namespace Newtonsoft.Json.Tests.Serialization [TestFixture] public class DynamicTests : TestFixtureBase { - public class DynamicChildObject - { - public string Text { get; set; } - public int Integer { get; set; } - } - - public class TestDynamicObject : DynamicObject - { - private readonly Dictionary _members; - - public int Int; - [JsonProperty] - public bool Explicit; - public DynamicChildObject ChildObject { get; set; } - - internal Dictionary Members - { - get { return _members; } - } - - public TestDynamicObject() - { - _members = new Dictionary(); - } - - public override IEnumerable GetDynamicMemberNames() - { - return _members.Keys.Union(new[] { "Int", "ChildObject" }); - } - - public override bool TryConvert(ConvertBinder binder, out object result) - { - Type targetType = binder.Type; - - if (targetType == typeof(IDictionary) || - targetType == typeof(IDictionary)) - { - result = new Dictionary(_members); - return true; - } - else - { - return base.TryConvert(binder, out result); - } - } - - public override bool TryDeleteMember(DeleteMemberBinder binder) - { - return _members.Remove(binder.Name); - } - - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - return _members.TryGetValue(binder.Name, out result); - } - - public override bool TrySetMember(SetMemberBinder binder, object value) - { - _members[binder.Name] = value; - return true; - } - } - - public class ErrorSettingDynamicObject : DynamicObject - { - public override bool TrySetMember(SetMemberBinder binder, object value) - { - return false; - } - } - [Test] public void SerializeDynamicObject() { @@ -317,5 +246,76 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(false, foo.Values["retweeted"]); } } + + public class DynamicChildObject + { + public string Text { get; set; } + public int Integer { get; set; } + } + + public class TestDynamicObject : DynamicObject + { + private readonly Dictionary _members; + + public int Int; + [JsonProperty] + public bool Explicit; + public DynamicChildObject ChildObject { get; set; } + + internal Dictionary Members + { + get { return _members; } + } + + public TestDynamicObject() + { + _members = new Dictionary(); + } + + public override IEnumerable GetDynamicMemberNames() + { + return _members.Keys.Union(new[] { "Int", "ChildObject" }); + } + + public override bool TryConvert(ConvertBinder binder, out object result) + { + Type targetType = binder.Type; + + if (targetType == typeof(IDictionary) || + targetType == typeof(IDictionary)) + { + result = new Dictionary(_members); + return true; + } + else + { + return base.TryConvert(binder, out result); + } + } + + public override bool TryDeleteMember(DeleteMemberBinder binder) + { + return _members.Remove(binder.Name); + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + return _members.TryGetValue(binder.Name, out result); + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + _members[binder.Name] = value; + return true; + } + } + + public class ErrorSettingDynamicObject : DynamicObject + { + public override bool TrySetMember(SetMemberBinder binder, object value) + { + return false; + } + } } #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 3770ca0c..6326d53d 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -6447,6 +6447,26 @@ Parameter name: value", } #endif + [Test] + public void AdditionalContentAfterFinish() + { + ExceptionAssert.Throws( + "Additional text found in JSON string after finishing deserializing object.", + () => + { + string json = "[{},1]"; + + JsonSerializer serializer = new JsonSerializer(); + serializer.CheckAdditionalContent = true; + + var reader = new JsonTextReader(new StringReader(json)); + reader.Read(); + reader.Read(); + + serializer.Deserialize(reader, typeof (MyType)); + }); + } + [Test] public void DeserializeRelativeUri() { diff --git a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs index 75e3fb67..3e394b0f 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs @@ -552,7 +552,7 @@ namespace Newtonsoft.Json.Tests.Serialization { IList errors = new List(); - const string input = "{\"events\":[{\"code\":64411,\"prio"; + const string input = "{\"events\":[{\"code\":64411},{\"code\":64411,\"prio"; const int maxDepth = 256; using (var jsonTextReader = new JsonTextReader(new StringReader(input)) {MaxDepth = maxDepth}) @@ -563,14 +563,84 @@ namespace Newtonsoft.Json.Tests.Serialization errors.Add(e.ErrorContext.Error.Message); e.ErrorContext.Handled = true; }; - - jsonSerializer.Deserialize(jsonTextReader, typeof (LogMessage)); + + LogMessage logMessage = jsonSerializer.Deserialize(jsonTextReader); + + Assert.IsNotNull(logMessage.Events); + Assert.AreEqual(1, logMessage.Events.Count); + Assert.AreEqual("64411", logMessage.Events[0].Code); + } + + Assert.AreEqual(3, errors.Count); + Assert.AreEqual(@"Unterminated string. Expected delimiter: "". Path 'events[1].code', line 1, position 45.", errors[0]); + Assert.AreEqual(@"Unexpected end when deserializing array. Path 'events[1].code', line 1, position 45.", errors[1]); + Assert.AreEqual(@"Unexpected end when deserializing object. Path 'events[1].code', line 1, position 45.", errors[2]); + } + + [Test] + public void ErrorHandlingEndOfContentDictionary() + { + IList errors = new List(); + + const string input = "{\"events\":{\"code\":64411},\"events2\":{\"code\":64412,"; + + const int maxDepth = 256; + using (var jsonTextReader = new JsonTextReader(new StringReader(input)) {MaxDepth = maxDepth}) + { + JsonSerializer jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings {MaxDepth = maxDepth}); + jsonSerializer.Error += (sender, e) => + { + errors.Add(e.ErrorContext.Error.Message); + e.ErrorContext.Handled = true; + }; + + IDictionary logEvents = jsonSerializer.Deserialize>(jsonTextReader); + + Assert.IsNotNull(logEvents); + Assert.AreEqual(2, logEvents.Count); + Assert.AreEqual("64411", logEvents["events"].Code); + Assert.AreEqual("64412", logEvents["events2"].Code); } Assert.AreEqual(2, errors.Count); - Assert.AreEqual(@"Unterminated string. Expected delimiter: "". Path 'events[0].code', line 1, position 30.", errors[0]); - Assert.AreEqual(@"Unexpected end when deserializing object. Path 'events[0].code', line 1, position 30.", errors[1]); + Assert.AreEqual(@"Unexpected end when deserializing object. Path 'events2.code', line 1, position 49.", errors[0]); + Assert.AreEqual(@"Unexpected end when deserializing object. Path 'events2.code', line 1, position 49.", errors[1]); } + +#if !(NET35 || NET20 || WINDOWS_PHONE || PORTABLE) + [Test] + public void ErrorHandlingEndOfContentDynamic() + { + IList errors = new List(); + + string json = @"{ + ""Explicit"": true, + ""Decimal"": 99.9, + ""Int"": 1, + ""ChildObject"": { + ""Integer"": 123"; + + TestDynamicObject newDynamicObject = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + Error = (sender, e) => + { + errors.Add(e.ErrorContext.Error.Message); + e.ErrorContext.Handled = true; + } + }); + Assert.AreEqual(true, newDynamicObject.Explicit); + + dynamic d = newDynamicObject; + + Assert.AreEqual(99.9, d.Decimal); + Assert.AreEqual(1, d.Int); + Assert.AreEqual(123, d.ChildObject.Integer); + + Assert.AreEqual(2, errors.Count); + Assert.AreEqual(@"Unexpected end when deserializing object. Path 'ChildObject.Integer', line 6, position 19.", errors[0]); + Assert.AreEqual(@"Unexpected end when deserializing object. Path 'ChildObject.Integer', line 6, position 19.", errors[1]); + } +#endif } public class ThrowingReader : TextReader @@ -639,6 +709,7 @@ namespace Newtonsoft.Json.Tests.Serialization public class LogMessage { public string DeviceId { get; set; } + public IList Events { get; set; } } public class LogEvent diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index c71e8475..47251bca 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -91,7 +91,7 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.5.8.15201")] +[assembly: AssemblyFileVersion("4.5.8.15204")] #endif [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 3bd68727..819cd6f4 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -813,6 +813,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ JsonConverter dictionaryValueConverter = contract.ItemConverter ?? GetConverter(contract.ItemContract, null, contract, containerProperty); + bool finished = false; do { switch (reader.TokenType) @@ -852,15 +853,18 @@ To fix this error either change the JSON to a {1} or change the deserialized typ case JsonToken.Comment: break; case JsonToken.EndObject: - contract.InvokeOnDeserialized(dictionary, Serializer.Context); - - return dictionary; + finished = true; + break; default: throw JsonSerializationException.Create(reader, "Unexpected token when deserializing object: " + reader.TokenType); } - } while (reader.Read()); + } while (!finished && reader.Read()); - throw JsonSerializationException.Create(reader, "Unexpected end when deserializing object."); + if (!finished) + ThrowUnexpectedEndException(reader, contract, dictionary, "Unexpected end when deserializing object."); + + contract.InvokeOnDeserialized(dictionary, Serializer.Context); + return dictionary; } private object PopulateMultidimensionalArray(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, string id) @@ -880,7 +884,8 @@ To fix this error either change the JSON to a {1} or change the deserialized typ listStack.Push(list); IList currentList = list; - while (true) + bool finished = false; + do { int initialDepth = reader.Depth; @@ -962,9 +967,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ } else { - contract.InvokeOnDeserialized(list, Serializer.Context); - - return list; + finished = true; } break; case JsonToken.Comment: @@ -978,9 +981,28 @@ To fix this error either change the JSON to a {1} or change the deserialized typ break; } } - } + } while (!finished); - throw JsonSerializationException.Create(reader, "Unexpected end when deserializing array."); + if (!finished) + ThrowUnexpectedEndException(reader, contract, list, "Unexpected end when deserializing array."); + + contract.InvokeOnDeserialized(list, Serializer.Context); + return list; + } + + private void ThrowUnexpectedEndException(JsonReader reader, JsonContract contract, object currentObject, string message) + { + try + { + throw JsonSerializationException.Create(reader, message); + } + catch (Exception ex) + { + if (IsErrorHandled(currentObject, contract, null, reader.Path, ex)) + HandleError(reader, false, 0); + else + throw; + } } private object PopulateList(IWrappedCollection wrappedList, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, string id) @@ -1006,7 +1028,8 @@ To fix this error either change the JSON to a {1} or change the deserialized typ int? previousErrorIndex = null; - while (true) + bool finished = false; + do { try { @@ -1015,9 +1038,8 @@ To fix this error either change the JSON to a {1} or change the deserialized typ switch (reader.TokenType) { case JsonToken.EndArray: - contract.InvokeOnDeserialized(list, Serializer.Context); - - return list; + finished = true; + break; case JsonToken.Comment: break; default: @@ -1061,9 +1083,13 @@ To fix this error either change the JSON to a {1} or change the deserialized typ throw; } } - } + } while (!finished); - throw JsonSerializationException.Create(reader, "Unexpected end when deserializing array."); + if (!finished) + ThrowUnexpectedEndException(reader, contract, list, "Unexpected end when deserializing array."); + + contract.InvokeOnDeserialized(list, Serializer.Context); + return list; } #if !(SILVERLIGHT || NETFX_CORE || PORTABLE) @@ -1080,7 +1106,7 @@ To fix this error either change the environment to be fully trusted, change the SerializationInfo serializationInfo = new SerializationInfo(contract.UnderlyingType, GetFormatterConverter()); - bool exit = false; + bool finished = false; do { switch (reader.TokenType) @@ -1095,12 +1121,15 @@ To fix this error either change the environment to be fully trusted, change the case JsonToken.Comment: break; case JsonToken.EndObject: - exit = true; + finished = true; break; default: throw JsonSerializationException.Create(reader, "Unexpected token when deserializing object: " + reader.TokenType); } - } while (!exit && reader.Read()); + } while (!finished && reader.Read()); + + if (!finished) + ThrowUnexpectedEndException(reader, contract, serializationInfo, "Unexpected end when deserializing object."); if (contract.ISerializableCreator == null) throw JsonSerializationException.Create(reader, "ISerializable type '{0}' does not have a valid constructor. To correctly implement ISerializable a constructor that takes SerializationInfo and StreamingContext parameters should be present.".FormatWith(CultureInfo.InvariantCulture, objectType)); @@ -1139,7 +1168,7 @@ To fix this error either change the environment to be fully trusted, change the int initialDepth = reader.Depth; - bool exit = false; + bool finished = false; do { switch (reader.TokenType) @@ -1189,12 +1218,15 @@ To fix this error either change the environment to be fully trusted, change the } break; case JsonToken.EndObject: - exit = true; + finished = true; break; default: throw JsonSerializationException.Create(reader, "Unexpected token when deserializing object: " + reader.TokenType); } - } while (!exit && reader.Read()); + } while (!finished && reader.Read()); + + if (!finished) + ThrowUnexpectedEndException(reader, contract, newObject, "Unexpected end when deserializing object."); contract.InvokeOnDeserialized(newObject, Serializer.Context); @@ -1450,6 +1482,7 @@ To fix this error either change the environment to be fully trusted, change the int initialDepth = reader.Depth; + bool finished = false; do { switch (reader.TokenType) @@ -1495,21 +1528,23 @@ To fix this error either change the environment to be fully trusted, change the } break; case JsonToken.EndObject: - { - EndObject(newObject, reader, contract, initialDepth, propertiesPresence); - - contract.InvokeOnDeserialized(newObject, Serializer.Context); - return newObject; - } + finished = true; + break; case JsonToken.Comment: // ignore break; default: throw JsonSerializationException.Create(reader, "Unexpected token when deserializing object: " + reader.TokenType); } - } while (reader.Read()); + } while (!finished && reader.Read()); - throw JsonSerializationException.Create(reader, "Unexpected end when deserializing object."); + if (!finished) + ThrowUnexpectedEndException(reader, contract, newObject, "Unexpected end when deserializing object."); + + EndObject(newObject, reader, contract, initialDepth, propertiesPresence); + + contract.InvokeOnDeserialized(newObject, Serializer.Context); + return newObject; } private void EndObject(object newObject, JsonReader reader, JsonObjectContract contract, int initialDepth, Dictionary propertiesPresence)