From be82ab9f0b7e74ab77921ce5c41b78b3471d0d9d Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 2 Aug 2012 20:50:44 +1200 Subject: [PATCH] -Fixed infinite loop error caused by error handling at the end of JSON content --- Doc/SerializationGuide.aml | 11 +- .../SerializationErrorHandlingTests.cs | 176 +++++++++++------- .../JsonSerializerInternalReader.cs | 3 +- 3 files changed, 116 insertions(+), 74 deletions(-) diff --git a/Doc/SerializationGuide.aml b/Doc/SerializationGuide.aml index 3ddb6434..ea29dc1b 100644 --- a/Doc/SerializationGuide.aml +++ b/Doc/SerializationGuide.aml @@ -276,13 +276,20 @@ - Serialization of values that are convertible by a JsonConverter (i.e. CanConvert returns true for its type) is + Serialization of values that are convertible by a T:Newtonsoft.Json.JsonConverter + (i.e. CanConvert returns true for its type) is completely overridden by the JsonConverter. The test to see whether a value can be converted by a JsonSerializer takes precedence over all other tests. - JsonConverters can be defined and specified in a number of places: in an attribute on a member, in an attribute + JsonConverters can be defined and specified in a number of places: in an attribute on a member, in an attribute on a class and added to the JsonSerializer's converters collection. The priority of which JsonConverter is used is the JsonConverter defined by attribute on a member then the JsonConverter defined by an attribute on a class and finally any converters passed to the JsonSerializer. + + + Because a JsonConverter creates a new value a converter will not work with readonly properties because there is no way to assign the new + value to the property. Either change the property to have a public setter or place a JsonPropertyAttribute or DataMemberAttribute on the property. + + diff --git a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs index fbfdf416..75e3fb67 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs @@ -259,14 +259,14 @@ namespace Newtonsoft.Json.Tests.Serialization List errors = new List(); JsonSerializer serializer = JsonSerializer.Create(new JsonSerializerSettings - { - Error = delegate(object sender, ErrorEventArgs args) { - errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message); - args.ErrorContext.Handled = true; - }, - Converters = { new IsoDateTimeConverter() } - }); + Error = delegate(object sender, ErrorEventArgs args) + { + errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message); + args.ErrorContext.Handled = true; + }, + Converters = {new IsoDateTimeConverter()} + }); var c = serializer.Deserialize>(new JsonTextReader(new StringReader(@"[ ""2009-09-09T00:00:00Z"", ""I am not a date and will error!"", @@ -374,13 +374,13 @@ namespace Newtonsoft.Json.Tests.Serialization { JsonSerializer serializer = new JsonSerializer(); serializer.Error += delegate(object sender, ErrorEventArgs args) - { - // only log an error once - if (args.CurrentObject == args.ErrorContext.OriginalObject) - errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message); - }; + { + // only log an error once + if (args.CurrentObject == args.ErrorContext.OriginalObject) + errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message); + }; - serializer.Deserialize(new StringReader(json), typeof(List>)); + serializer.Deserialize(new StringReader(json), typeof (List>)); } catch (Exception ex) { @@ -405,7 +405,7 @@ namespace Newtonsoft.Json.Tests.Serialization args.ErrorContext.Handled = true; }; serializer.Deserialize(new JsonTextReader(new StringReader(json)), typeof (MyTypeWithRequiredMembers)); - + Assert.AreEqual(2, errors.Count); Assert.AreEqual(" - Required1 - Required property 'Required1' not found in JSON. Path '', line 1, position 2.", errors[0]); Assert.AreEqual(" - Required2 - Required property 'Required2' not found in JSON. Path '', line 1, position 2.", errors[1]); @@ -420,12 +420,12 @@ namespace Newtonsoft.Json.Tests.Serialization JsonSerializer serializer = new JsonSerializer(); serializer.Error += delegate(object sender, ErrorEventArgs args) - { - errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message); - args.ErrorContext.Handled = true; - }; + { + errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message); + args.ErrorContext.Handled = true; + }; - serializer.Deserialize(new JsonTextReader(new StringReader(json)), typeof(int[])); + serializer.Deserialize(new JsonTextReader(new StringReader(json)), typeof (int[])); Assert.AreEqual(2, errors.Count); Assert.AreEqual("[0] - 0 - Could not convert string to integer: a. Path '[0]', line 1, position 4.", errors[0]); @@ -441,12 +441,12 @@ namespace Newtonsoft.Json.Tests.Serialization JsonSerializer serializer = new JsonSerializer(); serializer.Error += delegate(object sender, ErrorEventArgs args) - { - errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message); - args.ErrorContext.Handled = true; - }; + { + errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message); + args.ErrorContext.Handled = true; + }; - serializer.Deserialize(new JsonTextReader(new StringReader(json)), typeof(int[,])); + serializer.Deserialize(new JsonTextReader(new StringReader(json)), typeof (int[,])); Assert.AreEqual(2, errors.Count); Assert.AreEqual("[0][0] - 0 - Could not convert string to integer: a. Path '[0][0]', line 1, position 5.", errors[0]); @@ -457,15 +457,15 @@ namespace Newtonsoft.Json.Tests.Serialization public void ErrorHandlingAndAvoidingRecursiveDepthError() { string json = "{'A':{'A':{'A':{'A':{'A':{}}}}}}"; - JsonSerializer serializer = new JsonSerializer() { }; + JsonSerializer serializer = new JsonSerializer() {}; IList errors = new List(); serializer.Error += (sender, e) => - { - e.ErrorContext.Handled = true; - errors.Add(e.ErrorContext.Path); - }; + { + e.ErrorContext.Handled = true; + errors.Add(e.ErrorContext.Path); + }; - serializer.Deserialize(new JsonTextReader(new StringReader(json)) { MaxDepth = 3 }); + serializer.Deserialize(new JsonTextReader(new StringReader(json)) {MaxDepth = 3}); Assert.AreEqual(1, errors.Count); Assert.AreEqual("A.A.A", errors[0]); @@ -515,7 +515,7 @@ namespace Newtonsoft.Json.Tests.Serialization }); Assert.IsNull(o); - + Assert.AreEqual(3, errors.Count); Assert.AreEqual("Unexpected character encountered while parsing value: x. Path '[0]', line 1, position 3.", errors[0]); Assert.AreEqual("Unexpected character encountered while parsing value: x. Path '[0]', line 1, position 3.", errors[1]); @@ -530,13 +530,13 @@ namespace Newtonsoft.Json.Tests.Serialization Dictionary o = JsonConvert.DeserializeObject>( "{'badarray':[0,x,2],'goodarray':[0,1,2]}", new JsonSerializerSettings - { - Error = (sender, arg) => { - errors.Add(arg.ErrorContext.Error.Message); - arg.ErrorContext.Handled = true; - } - }); + Error = (sender, arg) => + { + errors.Add(arg.ErrorContext.Error.Message); + arg.ErrorContext.Handled = true; + } + }); Assert.IsNull(o); @@ -547,48 +547,73 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual("Unexpected character encountered while parsing value: x. Path 'badarray[0]', line 1, position 15.", errors[3]); } - public class ThrowingReader : TextReader + [Test] + public void ErrorHandlingEndOfContent() { - int _position = 0; - static string element = "{\"FirstName\":\"Din\",\"LastName\":\"Rav\",\"Item\":{\"ItemName\":\"temp\"}}"; - bool _firstRead = true; - bool _readComma = false; + IList errors = new List(); - public ThrowingReader() + const string input = "{\"events\":[{\"code\":64411,\"prio"; + + 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; + }; + + jsonSerializer.Deserialize(jsonTextReader, typeof (LogMessage)); } - public override int Read(char[] buffer, int index, int count) + 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]); + } + } + + public class ThrowingReader : TextReader + { + private int _position = 0; + private static string element = "{\"FirstName\":\"Din\",\"LastName\":\"Rav\",\"Item\":{\"ItemName\":\"temp\"}}"; + private bool _firstRead = true; + private bool _readComma = false; + + public ThrowingReader() + { + } + + public override int Read(char[] buffer, int index, int count) + { + char[] temp = new char[buffer.Length]; + int charsRead = 0; + if (_firstRead) { - char[] temp = new char[buffer.Length]; - int charsRead = 0; - if (_firstRead) + charsRead = new StringReader("[").Read(temp, index, count); + _firstRead = false; + } + else + { + if (_readComma) { - charsRead = new StringReader("[").Read(temp, index, count); - _firstRead = false; + charsRead = new StringReader(",").Read(temp, index, count); + _readComma = false; } else { - if (_readComma) - { - charsRead = new StringReader(",").Read(temp, index, count); - _readComma = false; - } - else - { - charsRead = new StringReader(element).Read(temp, index, count); - _readComma = true; - } + charsRead = new StringReader(element).Read(temp, index, count); + _readComma = true; } - - _position += charsRead; - if (_position > 65536) - { - throw new Exception("too far"); - } - Array.Copy(temp, index, buffer, index, charsRead); - return charsRead; } + + _position += charsRead; + if (_position > 65536) + { + throw new Exception("too far"); + } + Array.Copy(temp, index, buffer, index, charsRead); + return charsRead; } } @@ -607,9 +632,18 @@ namespace Newtonsoft.Json.Tests.Serialization [JsonObject] public class MyTypeWithRequiredMembers { - [JsonProperty(Required = Required.AllowNull)] - public string Required1; - [JsonProperty(Required = Required.AllowNull)] - public string Required2; + [JsonProperty(Required = Required.AllowNull)] public string Required1; + [JsonProperty(Required = Required.AllowNull)] public string Required2; + } + + public class LogMessage + { + public string DeviceId { get; set; } + } + + public class LogEvent + { + public string Code { get; set; } + public int Priority { get; set; } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index c274cbfb..3bd68727 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -1577,7 +1577,8 @@ To fix this error either change the environment to be fully trusted, change the while (reader.Depth > (initialDepth + 1)) { - reader.Read(); + if (!reader.Read()) + break; } } }