0

I'm currently using similar code to deserialize data from the stream.

    public static MyClass Parse(Stream stream)
    {
        var serializer = new JsonSerializer();
        using (var sr = new StreamReader(stream))
        using (var jsonTextReader = new JsonTextReader(sr))
        {
            var result = serializer.Deserialize<MyClass>(jsonTextReader);
            return result;
        }
    }

Is it possible to support cancelation of started deserialization by user, because this process can take a long for huge json files and slow connection ?

Milos
  • 1,353
  • 2
  • 18
  • 37
  • Not entirely sure I understand what you want to do. If you are running with a single thread, how will the user be able to cancel? If running with multiple threads (in .Net 3.5) why not use a [`BackgroundWorker`](https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.90).aspx)? See also https://msdn.microsoft.com/en-us/library/ywkkz4s1%28v=vs.100%29.aspx – dbc Apr 14 '16 at 16:30

2 Answers2

1

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.

dbc
  • 104,963
  • 20
  • 228
  • 340
0

If your serializer does not support cancellation, you can do the following instead:

  • support cancellation in the upper layer that fetches the data

E.g. for WebClient one would leverage WebClient.CancelAsync, etc ...

aybe
  • 15,516
  • 9
  • 57
  • 105