-Added CheckAdditionalContent to JsonSerializer/JsonSerializerSettings
-Fixed bad JSON in an array with error handling creating an infinite loop -JsonTextReader no longer returns no content after consecutive underlying content read failures
This commit is contained in:
@@ -2529,7 +2529,8 @@ bye", reader.Value);
|
||||
|
||||
toggleReaderError.Error = false;
|
||||
|
||||
Assert.IsFalse(jsonTextReader.Read());
|
||||
//a reader use to skip to the end after 3 errors in a row
|
||||
//Assert.IsFalse(jsonTextReader.Read());
|
||||
}
|
||||
|
||||
public class ToggleReaderError : TextReader
|
||||
|
||||
@@ -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.5.14913")]
|
||||
[assembly: AssemblyFileVersion("4.5.5.14917")]
|
||||
#endif
|
||||
|
||||
@@ -6256,6 +6256,28 @@ Parameter name: value",
|
||||
}", json);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Test]
|
||||
public void CheckAdditionalContent()
|
||||
{
|
||||
string json = "{one:1}{}";
|
||||
|
||||
JsonSerializerSettings settings = new JsonSerializerSettings();
|
||||
JsonSerializer s = JsonSerializer.Create(settings);
|
||||
IDictionary<string, int> o = s.Deserialize<Dictionary<string, int>>(new JsonTextReader(new StringReader(json)));
|
||||
|
||||
Assert.IsNotNull(o);
|
||||
Assert.AreEqual(1, o["one"]);
|
||||
|
||||
settings.CheckAdditionalContent = true;
|
||||
s = JsonSerializer.Create(settings);
|
||||
ExceptionAssert.Throws<JsonReaderException>(
|
||||
"Additional text encountered after finished reading JSON content: {. Path '', line 1, position 7.",
|
||||
() =>
|
||||
{
|
||||
s.Deserialize<Dictionary<string, int>>(new JsonTextReader(new StringReader(json)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#if !(SILVERLIGHT || NETFX_CORE || PORTABLE)
|
||||
|
||||
@@ -165,8 +165,16 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
{
|
||||
List<string> errors = new List<string>();
|
||||
|
||||
List<DateTime> c = JsonConvert.DeserializeObject<List<DateTime>>(
|
||||
@"[
|
||||
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() }
|
||||
});
|
||||
var c = serializer.Deserialize<List<DateTime>>(new JsonTextReader(new StringReader(@"[
|
||||
""2009-09-09T00:00:00Z"",
|
||||
""I am not a date and will error!"",
|
||||
[
|
||||
@@ -175,16 +183,8 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
""1977-02-20T00:00:00Z"",
|
||||
null,
|
||||
""2000-12-01T00:00:00Z""
|
||||
]",
|
||||
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()}
|
||||
});
|
||||
]")));
|
||||
|
||||
|
||||
// 2009-09-09T00:00:00Z
|
||||
// 1977-02-20T00:00:00Z
|
||||
@@ -377,11 +377,60 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
ErrorPerson[] result = serializer.Deserialize<ErrorPerson[]>(new JsonTextReader(new ThrowingReader()));
|
||||
|
||||
Assert.IsNull(result);
|
||||
Assert.AreEqual(4, errors.Count);
|
||||
Assert.AreEqual(3, errors.Count);
|
||||
Assert.AreEqual("too far", errors[0]);
|
||||
Assert.AreEqual("too far", errors[1]);
|
||||
Assert.AreEqual("too far", errors[2]);
|
||||
Assert.AreEqual("Unexpected end when deserializing array. Path '[1023]', line 1, position 65536.", errors[3]);
|
||||
Assert.AreEqual("Infinite loop detected from error handling. Path '[1023]', line 1, position 65536.", errors[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InfiniteLoopArrayHandling()
|
||||
{
|
||||
IList<string> errors = new List<string>();
|
||||
|
||||
object o = JsonConvert.DeserializeObject(
|
||||
"[0,x]",
|
||||
typeof (int[]),
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
Error = (sender, arg) =>
|
||||
{
|
||||
errors.Add(arg.ErrorContext.Error.Message);
|
||||
arg.ErrorContext.Handled = true;
|
||||
}
|
||||
});
|
||||
|
||||
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]);
|
||||
Assert.AreEqual("Infinite loop detected from error handling. Path '[0]', line 1, position 3.", errors[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InfiniteLoopArrayHandlingInObject()
|
||||
{
|
||||
IList<string> errors = new List<string>();
|
||||
|
||||
Dictionary<string, int[]> o = JsonConvert.DeserializeObject<Dictionary<string, int[]>>(
|
||||
"{'badarray':[0,x,2],'goodarray':[0,1,2]}",
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
Error = (sender, arg) =>
|
||||
{
|
||||
errors.Add(arg.ErrorContext.Error.Message);
|
||||
arg.ErrorContext.Handled = true;
|
||||
}
|
||||
});
|
||||
|
||||
Assert.IsNull(o);
|
||||
|
||||
Assert.AreEqual(4, errors.Count);
|
||||
Assert.AreEqual("Unexpected character encountered while parsing value: x. Path 'badarray[0]', line 1, position 15.", errors[0]);
|
||||
Assert.AreEqual("Unexpected character encountered while parsing value: x. Path 'badarray[0]', line 1, position 15.", errors[1]);
|
||||
Assert.AreEqual("Infinite loop detected from error handling. Path 'badarray[0]', line 1, position 15.", errors[2]);
|
||||
Assert.AreEqual("Unexpected character encountered while parsing value: x. Path 'badarray[0]', line 1, position 15.", errors[3]);
|
||||
}
|
||||
|
||||
public class ThrowingReader : TextReader
|
||||
|
||||
@@ -918,17 +918,11 @@ namespace Newtonsoft.Json
|
||||
StringReader sr = new StringReader(value);
|
||||
JsonSerializer jsonSerializer = JsonSerializer.Create(settings);
|
||||
|
||||
object deserializedValue;
|
||||
// by default DeserializeObject should check for additional content
|
||||
if (!jsonSerializer.IsCheckAdditionalContentSet())
|
||||
jsonSerializer.CheckAdditionalContent = true;
|
||||
|
||||
using (JsonReader jsonReader = new JsonTextReader(sr))
|
||||
{
|
||||
deserializedValue = jsonSerializer.Deserialize(jsonReader, type);
|
||||
|
||||
if (jsonReader.Read() && jsonReader.TokenType != JsonToken.Comment)
|
||||
throw new JsonSerializationException("Additional text found in JSON string after finishing deserializing object.");
|
||||
}
|
||||
|
||||
return deserializedValue;
|
||||
return jsonSerializer.Deserialize(new JsonTextReader(sr), type);
|
||||
}
|
||||
|
||||
#if !(NET20 || NET35 || SILVERLIGHT || PORTABLE)
|
||||
|
||||
@@ -112,6 +112,7 @@ namespace Newtonsoft.Json
|
||||
private int? _maxDepth;
|
||||
private bool _hasExceededMaxDepth;
|
||||
internal DateParseHandling _dateParseHandling;
|
||||
private readonly List<JsonPosition> _stack;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current reader state.
|
||||
@@ -122,8 +123,6 @@ namespace Newtonsoft.Json
|
||||
get { return _currentState; }
|
||||
}
|
||||
|
||||
private readonly List<JsonPosition> _stack;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the underlying stream or
|
||||
/// <see cref="TextReader"/> should be closed when the reader is closed.
|
||||
@@ -239,6 +238,14 @@ namespace Newtonsoft.Json
|
||||
set { _culture = value; }
|
||||
}
|
||||
|
||||
internal JsonPosition GetPosition(int depth)
|
||||
{
|
||||
if (depth < _stack.Count)
|
||||
return _stack[depth];
|
||||
|
||||
return _currentPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonReader"/> class with the specified <see cref="TextReader"/>.
|
||||
/// </summary>
|
||||
@@ -705,8 +712,6 @@ namespace Newtonsoft.Json
|
||||
/// <param name="value">The value.</param>
|
||||
protected void SetToken(JsonToken newToken, object value)
|
||||
{
|
||||
JsonToken previousToken = _tokenType;
|
||||
|
||||
_tokenType = newToken;
|
||||
_value = value;
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace Newtonsoft.Json
|
||||
private CultureInfo _culture;
|
||||
private int? _maxDepth;
|
||||
private bool _maxDepthSet;
|
||||
private bool? _checkAdditionalContent;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the <see cref="JsonSerializer"/> errors during serialization and deserialization.
|
||||
@@ -348,6 +349,18 @@ namespace Newtonsoft.Json
|
||||
_maxDepthSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CheckAdditionalContent
|
||||
{
|
||||
get { return _checkAdditionalContent ?? JsonSerializerSettings.DefaultCheckAdditionalContent; }
|
||||
set { _checkAdditionalContent = value; }
|
||||
}
|
||||
|
||||
internal bool IsCheckAdditionalContentSet()
|
||||
{
|
||||
return (_checkAdditionalContent != null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
@@ -392,6 +405,7 @@ namespace Newtonsoft.Json
|
||||
jsonSerializer.DefaultValueHandling = settings.DefaultValueHandling;
|
||||
jsonSerializer.ConstructorHandling = settings.ConstructorHandling;
|
||||
jsonSerializer.Context = settings.Context;
|
||||
jsonSerializer._checkAdditionalContent = settings._checkAdditionalContent;
|
||||
|
||||
// reader/writer specific
|
||||
// unset values won't override reader/writer set values
|
||||
@@ -523,7 +537,7 @@ namespace Newtonsoft.Json
|
||||
}
|
||||
|
||||
JsonSerializerInternalReader serializerReader = new JsonSerializerInternalReader(this);
|
||||
object value = serializerReader.Deserialize(reader, objectType);
|
||||
object value = serializerReader.Deserialize(reader, objectType, CheckAdditionalContent);
|
||||
|
||||
// reset reader back to previous options
|
||||
if (previousCulture != null)
|
||||
|
||||
@@ -53,12 +53,14 @@ namespace Newtonsoft.Json
|
||||
internal const DateTimeZoneHandling DefaultDateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
|
||||
internal const DateParseHandling DefaultDateParseHandling = DateParseHandling.DateTime;
|
||||
internal static readonly CultureInfo DefaultCulture;
|
||||
internal const bool DefaultCheckAdditionalContent = false;
|
||||
|
||||
internal Formatting? _formatting;
|
||||
internal DateFormatHandling? _dateFormatHandling;
|
||||
internal DateTimeZoneHandling? _dateTimeZoneHandling;
|
||||
internal DateParseHandling? _dateParseHandling;
|
||||
internal CultureInfo _culture;
|
||||
internal bool? _checkAdditionalContent;
|
||||
internal int? _maxDepth;
|
||||
internal bool _maxDepthSet;
|
||||
|
||||
@@ -214,6 +216,12 @@ namespace Newtonsoft.Json
|
||||
set { _culture = value; }
|
||||
}
|
||||
|
||||
public bool CheckAdditionalContent
|
||||
{
|
||||
get { return _checkAdditionalContent ?? DefaultCheckAdditionalContent; }
|
||||
set { _checkAdditionalContent = value; }
|
||||
}
|
||||
|
||||
static JsonSerializerSettings()
|
||||
{
|
||||
DefaultContext = new StreamingContext();
|
||||
|
||||
@@ -55,8 +55,6 @@ namespace Newtonsoft.Json
|
||||
private const char UnicodeReplacementChar = '\uFFFD';
|
||||
|
||||
private readonly TextReader _reader;
|
||||
private int _readerFailureCount;
|
||||
|
||||
private char[] _chars;
|
||||
private int _charsUsed;
|
||||
private int _charPos;
|
||||
@@ -76,10 +74,7 @@ namespace Newtonsoft.Json
|
||||
throw new ArgumentNullException("reader");
|
||||
|
||||
_reader = reader;
|
||||
_readerFailureCount = 0;
|
||||
|
||||
_lineNumber = 1;
|
||||
|
||||
_chars = new char[4097];
|
||||
}
|
||||
|
||||
@@ -326,25 +321,7 @@ namespace Newtonsoft.Json
|
||||
|
||||
int attemptCharReadCount = _chars.Length - _charsUsed - 1;
|
||||
|
||||
int charsRead;
|
||||
|
||||
try
|
||||
{
|
||||
charsRead = _reader.Read(_chars, _charsUsed, attemptCharReadCount);
|
||||
_readerFailureCount = 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// to prevent infinate loop when error handling is enabled
|
||||
if (!_isEndOfFile)
|
||||
{
|
||||
_readerFailureCount++;
|
||||
if (_readerFailureCount >= 3)
|
||||
_isEndOfFile = true;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
int charsRead = _reader.Read(_chars, _charsUsed, attemptCharReadCount);
|
||||
|
||||
_charsUsed += charsRead;
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ using System.Security;
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("4.5.0.0")]
|
||||
#if !PocketPC
|
||||
[assembly: AssemblyFileVersion("4.5.5.14913")]
|
||||
[assembly: AssemblyFileVersion("4.5.5.14917")]
|
||||
#endif
|
||||
|
||||
[assembly: CLSCompliant(true)]
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
return Serializer.ContractResolver.ResolveContract(type);
|
||||
}
|
||||
|
||||
public object Deserialize(JsonReader reader, Type objectType)
|
||||
public object Deserialize(JsonReader reader, Type objectType, bool checkAdditionalContent)
|
||||
{
|
||||
if (reader == null)
|
||||
throw new ArgumentNullException("reader");
|
||||
@@ -125,16 +125,26 @@ namespace Newtonsoft.Json.Serialization
|
||||
return null;
|
||||
}
|
||||
|
||||
if (converter != null && converter.CanRead)
|
||||
return converter.ReadJson(reader, objectType, null, GetInternalSerializer());
|
||||
object deserializedValue;
|
||||
|
||||
return CreateValueInternal(reader, objectType, contract, null, null, null, null);
|
||||
if (converter != null && converter.CanRead)
|
||||
deserializedValue = converter.ReadJson(reader, objectType, null, GetInternalSerializer());
|
||||
else
|
||||
deserializedValue = CreateValueInternal(reader, objectType, contract, null, null, null, null);
|
||||
|
||||
if (checkAdditionalContent)
|
||||
{
|
||||
if (reader.Read() && reader.TokenType != JsonToken.Comment)
|
||||
throw new JsonSerializationException("Additional text found in JSON string after finishing deserializing object.");
|
||||
}
|
||||
|
||||
return deserializedValue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(null, contract, null, reader.Path, ex))
|
||||
{
|
||||
HandleError(reader, 0);
|
||||
HandleError(reader, false, 0);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
@@ -793,7 +803,7 @@ To force JSON arrays to deserialize add the JsonArrayAttribute to the type.".For
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(dictionary, contract, keyValue, reader.Path, ex))
|
||||
HandleError(reader, initialDepth);
|
||||
HandleError(reader, true, initialDepth);
|
||||
else
|
||||
throw;
|
||||
}
|
||||
@@ -829,11 +839,12 @@ To force JSON arrays to deserialize add the JsonArrayAttribute to the type.".For
|
||||
contract.InvokeOnDeserializing(list, Serializer.Context);
|
||||
|
||||
int initialDepth = reader.Depth;
|
||||
int index = 0;
|
||||
|
||||
JsonContract collectionItemContract = GetContractSafe(contract.CollectionItemType);
|
||||
JsonConverter collectionItemConverter = GetConverter(collectionItemContract, null, contract, containerProperty);
|
||||
|
||||
int? previousErrorIndex = null;
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
@@ -867,14 +878,27 @@ To force JSON arrays to deserialize add the JsonArrayAttribute to the type.".For
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(list, contract, index, reader.Path, ex))
|
||||
HandleError(reader, initialDepth);
|
||||
JsonPosition errorPosition = reader.GetPosition(initialDepth);
|
||||
|
||||
if (IsErrorHandled(list, contract, errorPosition.Position, reader.Path, ex))
|
||||
{
|
||||
HandleError(reader, true, initialDepth);
|
||||
|
||||
if (previousErrorIndex != null && previousErrorIndex == errorPosition.Position)
|
||||
{
|
||||
// reader index has not moved since previous error handling
|
||||
// break out of reading array to prevent infinite loop
|
||||
throw JsonSerializationException.Create(reader, "Infinite loop detected from error handling.", ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
previousErrorIndex = errorPosition.Position;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -997,7 +1021,7 @@ To fix this error either change the environment to be fully trusted, change the
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(newObject, contract, memberName, reader.Path, ex))
|
||||
HandleError(reader, initialDepth);
|
||||
HandleError(reader, true, initialDepth);
|
||||
else
|
||||
throw;
|
||||
}
|
||||
@@ -1297,7 +1321,7 @@ To fix this error either change the environment to be fully trusted, change the
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(newObject, contract, memberName, reader.Path, ex))
|
||||
HandleError(reader, initialDepth);
|
||||
HandleError(reader, true, initialDepth);
|
||||
else
|
||||
throw;
|
||||
}
|
||||
@@ -1357,7 +1381,7 @@ To fix this error either change the environment to be fully trusted, change the
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(newObject, contract, property.PropertyName, reader.Path, ex))
|
||||
HandleError(reader, initialDepth);
|
||||
HandleError(reader, true, initialDepth);
|
||||
else
|
||||
throw;
|
||||
}
|
||||
@@ -1376,15 +1400,18 @@ To fix this error either change the environment to be fully trusted, change the
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleError(JsonReader reader, int initialDepth)
|
||||
private void HandleError(JsonReader reader, bool readPasteError, int initialDepth)
|
||||
{
|
||||
ClearErrorContext();
|
||||
|
||||
reader.Skip();
|
||||
|
||||
while (reader.Depth > (initialDepth + 1))
|
||||
if (readPasteError)
|
||||
{
|
||||
reader.Read();
|
||||
reader.Skip();
|
||||
|
||||
while (reader.Depth > (initialDepth + 1))
|
||||
{
|
||||
reader.Read();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -162,6 +162,12 @@ namespace Newtonsoft.Json.Serialization
|
||||
set { _serializer.MaxDepth = value; }
|
||||
}
|
||||
|
||||
public override bool CheckAdditionalContent
|
||||
{
|
||||
get { return _serializer.CheckAdditionalContent; }
|
||||
set { _serializer.CheckAdditionalContent = value; }
|
||||
}
|
||||
|
||||
internal JsonSerializerInternalBase GetInternalSerializer()
|
||||
{
|
||||
if (_serializerReader != null)
|
||||
@@ -189,7 +195,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
internal override object DeserializeInternal(JsonReader reader, Type objectType)
|
||||
{
|
||||
if (_serializerReader != null)
|
||||
return _serializerReader.Deserialize(reader, objectType);
|
||||
return _serializerReader.Deserialize(reader, objectType, false);
|
||||
else
|
||||
return _serializer.Deserialize(reader, objectType);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user