取消 JSON.NET 流反序列化

Cancel JSON.NET stream deserialization

我目前正在使用类似的代码反序列化流中的数据。

    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;
        }
    }

是否可以支持取消用户开始的反序列化,因为对于巨大的 json 文件和缓慢的连接,此过程可能需要很长时间?

如果您的序列化程序不支持取消,您可以改为执行以下操作:

  • 支持上层取数据取消

例如对于 WebClient,一个人会利用 WebClient.CancelAsync,等等...

JsonTextReader 不支持开箱即用,但 Json.NET 确实正确处理(即重新抛出)来自较低级别代码的异常,因此您可以子类化 reader 和自己动手:

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)
    {
    }
}

然后像这样使用它:

    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;
        }
    }

然后,在更高的代码级别,捕获 JsonReaderCancelledException 异常。

请注意,如果您的 checkCancelled 方法正在检查可能在另一个线程中设置的 bool 标志,您必须将其声明为 volatile. See Is it safe to use a boolean flag to stop a thread from running in C#

你我想在将其投入生产之前测试性能。如果为每次读取调用 checkCancelled 委托的性能不佳,您可以为每 10 或 100 次读取调用一次。