-Fixed infinite loop from an input error when reading an array and error handling is catching the error
-Fixed base object error not being handled when deserializing
This commit is contained in:
@@ -2481,6 +2481,76 @@ bye", reader.Value);
|
||||
Assert.IsTrue(reader.Read());
|
||||
#endif
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ResetJsonTextReaderErrorCount()
|
||||
{
|
||||
ToggleReaderError toggleReaderError = new ToggleReaderError(new StringReader("{'first':1,'second':2,'third':3}"));
|
||||
JsonTextReader jsonTextReader = new JsonTextReader(toggleReaderError);
|
||||
|
||||
Assert.IsTrue(jsonTextReader.Read());
|
||||
|
||||
toggleReaderError.Error = true;
|
||||
|
||||
ExceptionAssert.Throws<Exception>(
|
||||
"Read error",
|
||||
() => jsonTextReader.Read());
|
||||
ExceptionAssert.Throws<Exception>(
|
||||
"Read error",
|
||||
() => jsonTextReader.Read());
|
||||
|
||||
toggleReaderError.Error = false;
|
||||
|
||||
Assert.IsTrue(jsonTextReader.Read());
|
||||
Assert.AreEqual("first", jsonTextReader.Value);
|
||||
|
||||
toggleReaderError.Error = true;
|
||||
|
||||
ExceptionAssert.Throws<Exception>(
|
||||
"Read error",
|
||||
() => jsonTextReader.Read());
|
||||
|
||||
toggleReaderError.Error = false;
|
||||
|
||||
Assert.IsTrue(jsonTextReader.Read());
|
||||
Assert.AreEqual(1, jsonTextReader.Value);
|
||||
|
||||
toggleReaderError.Error = true;
|
||||
|
||||
ExceptionAssert.Throws<Exception>(
|
||||
"Read error",
|
||||
() => jsonTextReader.Read());
|
||||
ExceptionAssert.Throws<Exception>(
|
||||
"Read error",
|
||||
() => jsonTextReader.Read());
|
||||
ExceptionAssert.Throws<Exception>(
|
||||
"Read error",
|
||||
() => jsonTextReader.Read());
|
||||
|
||||
toggleReaderError.Error = false;
|
||||
|
||||
Assert.IsFalse(jsonTextReader.Read());
|
||||
}
|
||||
|
||||
public class ToggleReaderError : TextReader
|
||||
{
|
||||
private readonly TextReader _inner;
|
||||
|
||||
public bool Error { get; set; }
|
||||
|
||||
public ToggleReaderError(TextReader inner)
|
||||
{
|
||||
_inner = inner;
|
||||
}
|
||||
|
||||
public override int Read(char[] buffer, int index, int count)
|
||||
{
|
||||
if (Error)
|
||||
throw new Exception("Read error");
|
||||
|
||||
return _inner.Read(buffer, index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SlowStream : Stream
|
||||
|
||||
@@ -361,6 +361,84 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
{
|
||||
public Nest A { get; set; }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InfiniteErrorHandlingLoopFromInputError()
|
||||
{
|
||||
IList<string> errors = new List<string>();
|
||||
|
||||
JsonSerializer serializer = new JsonSerializer();
|
||||
serializer.Error += (sender, e) =>
|
||||
{
|
||||
errors.Add(e.ErrorContext.Error.Message);
|
||||
e.ErrorContext.Handled = true;
|
||||
};
|
||||
|
||||
ErrorPerson[] result = serializer.Deserialize<ErrorPerson[]>(new JsonTextReader(new ThrowingReader()));
|
||||
|
||||
Assert.IsNull(result);
|
||||
Assert.AreEqual(4, 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]);
|
||||
}
|
||||
|
||||
public class ThrowingReader : TextReader
|
||||
{
|
||||
int _position = 0;
|
||||
static string element = "{\"FirstName\":\"Din\",\"LastName\":\"Rav\",\"Item\":{\"ItemName\":\"temp\"}}";
|
||||
bool _firstRead = true;
|
||||
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)
|
||||
{
|
||||
charsRead = new StringReader("[").Read(temp, index, count);
|
||||
_firstRead = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_readComma)
|
||||
{
|
||||
charsRead = new StringReader(",").Read(temp, index, count);
|
||||
_readComma = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ErrorPerson
|
||||
{
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public ErrorItem Item { get; set; }
|
||||
}
|
||||
|
||||
public class ErrorItem
|
||||
{
|
||||
public string ItemName { get; set; }
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
|
||||
@@ -55,6 +55,7 @@ namespace Newtonsoft.Json
|
||||
private const char UnicodeReplacementChar = '\uFFFD';
|
||||
|
||||
private readonly TextReader _reader;
|
||||
private int _readerFailureCount;
|
||||
|
||||
private char[] _chars;
|
||||
private int _charsUsed;
|
||||
@@ -75,6 +76,8 @@ namespace Newtonsoft.Json
|
||||
throw new ArgumentNullException("reader");
|
||||
|
||||
_reader = reader;
|
||||
_readerFailureCount = 0;
|
||||
|
||||
_lineNumber = 1;
|
||||
|
||||
_chars = new char[4097];
|
||||
@@ -323,7 +326,26 @@ namespace Newtonsoft.Json
|
||||
|
||||
int attemptCharReadCount = _chars.Length - _charsUsed - 1;
|
||||
|
||||
int charsRead = _reader.Read(_chars, _charsUsed, attemptCharReadCount);
|
||||
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;
|
||||
}
|
||||
|
||||
_charsUsed += charsRead;
|
||||
|
||||
if (charsRead == 0)
|
||||
|
||||
@@ -101,7 +101,8 @@ namespace Newtonsoft.Json.Serialization
|
||||
protected bool IsErrorHandled(object currentObject, JsonContract contract, object keyValue, string path, Exception ex)
|
||||
{
|
||||
ErrorContext errorContext = GetErrorContext(currentObject, keyValue, path, ex);
|
||||
contract.InvokeOnError(currentObject, Serializer.Context, errorContext);
|
||||
if (contract != null)
|
||||
contract.InvokeOnError(currentObject, Serializer.Context, errorContext);
|
||||
|
||||
if (!errorContext.Handled)
|
||||
Serializer.OnError(new ErrorEventArgs(currentObject, errorContext));
|
||||
|
||||
@@ -113,17 +113,32 @@ namespace Newtonsoft.Json.Serialization
|
||||
|
||||
JsonContract contract = GetContractSafe(objectType);
|
||||
|
||||
JsonConverter converter = GetConverter(contract, null, null);
|
||||
|
||||
if (reader.TokenType == JsonToken.None && !ReadForType(reader, contract, converter != null, false))
|
||||
try
|
||||
{
|
||||
if (contract != null && !contract.IsNullable)
|
||||
throw JsonSerializationException.Create(reader, "No JSON content found and type '{0}' is not nullable.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
|
||||
JsonConverter converter = GetConverter(contract, null, null);
|
||||
|
||||
return null;
|
||||
if (reader.TokenType == JsonToken.None && !ReadForType(reader, contract, converter != null, false))
|
||||
{
|
||||
if (contract != null && !contract.IsNullable)
|
||||
throw JsonSerializationException.Create(reader, "No JSON content found and type '{0}' is not nullable.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return CreateValueNonProperty(reader, objectType, contract, converter, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(null, contract, null, reader.Path, ex))
|
||||
{
|
||||
HandleError(reader, 0);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return CreateValueNonProperty(reader, objectType, contract, converter, null);
|
||||
}
|
||||
|
||||
private JsonSerializerProxy GetInternalSerializer()
|
||||
|
||||
Reference in New Issue
Block a user