JsonTextReader
does not support this out of the box, but Json.NET does correctly handle (i.e. rethrow) exceptions coming from lower level code, so you could subclass the reader and do it yourself:
public class CancellableJsonTextReader : JsonTextReader
{
protected Func<bool> CheckCancelled { get; set; }
public CancellableJsonTextReader(TextReader reader, Func<bool> checkCancelled)
: base(reader)
{
this.CheckCancelled = checkCancelled;
}
public bool IsCancelled { get; private set; }
public override bool Read()
{
DoCheckCancelled();
return base.Read();
}
public override bool? ReadAsBoolean()
{
DoCheckCancelled();
return base.ReadAsBoolean();
}
public override byte[] ReadAsBytes()
{
DoCheckCancelled();
return base.ReadAsBytes();
}
public override DateTime? ReadAsDateTime()
{
DoCheckCancelled();
return base.ReadAsDateTime();
}
public override DateTimeOffset? ReadAsDateTimeOffset()
{
DoCheckCancelled();
return base.ReadAsDateTimeOffset();
}
public override decimal? ReadAsDecimal()
{
DoCheckCancelled();
return base.ReadAsDecimal();
}
public override double? ReadAsDouble()
{
DoCheckCancelled();
return base.ReadAsDouble();
}
public override int? ReadAsInt32()
{
DoCheckCancelled();
return base.ReadAsInt32();
}
public override string ReadAsString()
{
DoCheckCancelled();
return base.ReadAsString();
}
private void DoCheckCancelled()
{
if (!IsCancelled && CheckCancelled != null)
IsCancelled = CheckCancelled();
if (IsCancelled)
{
throw new JsonReaderCancelledException();
}
}
}
public class JsonReaderCancelledException : JsonReaderException
{
public JsonReaderCancelledException() { }
public JsonReaderCancelledException(string message)
: base(message)
{
}
public JsonReaderCancelledException(string message, Exception innerException)
: base(message, innerException)
{
}
public JsonReaderCancelledException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
And then use it like:
public static T Parse<T>(Stream stream, Func<bool> checkCancelled)
{
var serializer = new JsonSerializer();
using (var sr = new StreamReader(stream))
using (var jsonTextReader = new CancellableJsonTextReader(sr, checkCancelled))
{
var result = serializer.Deserialize<T>(jsonTextReader);
return result;
}
}
Then, at a higher code level, catch the JsonReaderCancelledException
exception.
Note that, if your checkCancelled
method is checking a bool
flag that might be set in another thread, you must declare it as volatile
. See Is it safe to use a boolean flag to stop a thread from running in C#.
You my want to test the performance before putting this into production. If calling the checkCancelled
delegate for every read is not performant, you could do it for every 10 or 100 reads.