System.Text.Json 无法访问处置 jsonDocument

System.Text.Json Cannot access a dispose jsonDocument

有人可以帮我解决这个错误吗? 直到现在我都无法解决这个问题。我不知道问题出在哪里。

“无法访问已释放的对象。对象名称:'JsonDocument'”

我刚开始使用“Sytem.Text.Json”,这就是我仍在学习并想知道如何正确使用它的原因。

谢谢。

  public static async Task<JsonElement> ParseJsonData(string api, CancellationToken ct)
    {
        clientHandler = new HttpClientHandler()
        {
            UseProxy = Proxy.IsUseProxy ? true : false,
            Proxy = Proxy.IsUseProxy ? new WebProxy($"{Proxy.ProxyHost}:{Proxy.ProxyPort}") : null,
            //ServerCertificateCustomValidationCallback = (sender, certificate, chain, errors) => { return true; },
            // SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls
        };
        var uri = new Uri(api, UriKind.Absolute);
        utils.SetConnection(uri);
        client = new HttpClient(clientHandler);
        using (var request = new HttpRequestMessage(HttpMethod.Get, uri))
        {
            AddRequestHeaders(request, uri);
            return await ResponseMessage(request, ct);
        }
    }
    private static async Task<JsonElement> ResponseMessage(HttpRequestMessage request, CancellationToken ct)
    {
        using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false))
        {
            ct.ThrowIfCancellationRequested();
 
            using (var content = response.Content)
            {
                var stream = await content.ReadAsStreamAsync().ConfigureAwait(false);
                var json = await ParseStream(stream, response);

                return json.RootElement;
            }
        }
    }

    private static async Task<JsonDocument> ParseStream(Stream stream, HttpResponseMessage response)
    {
        if (stream == null || stream.CanRead == false)
        {
            return default;
        }

        HttpStatusCode status = response.StatusCode;
        StatusCode.status = status.ToString();
        StatusCode.value = (int)status;

        using (var json = await JsonDocument.ParseAsync(stream).ConfigureAwait(false))
        {
            if (!response.IsSuccessStatusCode)
            {
                throw new ApiException()
                {
                    Content = json.RootElement.ToString(),
                    StatusCode = status.ToString(),
                    value = (int)status,
                };
            }
            return json;
        }
    }

更新:(这是我试过的)

     public static async Task<JsonDocument> ParseJsonData(string api, CancellationToken ct)
        {
            clientHandler = new HttpClientHandler()
            {
                UseProxy = Proxy.IsUseProxy ? true : false,
                Proxy = Proxy.IsUseProxy ? new WebProxy($"{Proxy.ProxyHost}:{Proxy.ProxyPort}") : null,
                ServerCertificateCustomValidationCallback = (sender, certificate, chain, errors) => { return true; }
                // SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls
            };
            var uri = new Uri(api, UriKind.Absolute);
            utils.SetConnection(uri);
            client = new HttpClient(clientHandler);

            using (var request = new HttpRequestMessage(HttpMethod.Get, uri))
            {
                AddRequestHeaders(request, uri);
                return await ResponseMessage(request, ct);
            }
        }
        private static async Task<JsonDocument> ResponseMessage(HttpRequestMessage request, CancellationToken ct)
        {
            using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false))
            {
                ct.ThrowIfCancellationRequested();
                HttpStatusCode status = response.StatusCode;
                
                using (var content = response.Content)
                {
                    var stream = await content.ReadAsStreamAsync().ConfigureAwait(false);

                    if (stream == null || stream.CanRead == false) { return default; }

                    var options = new JsonDocumentOptions { AllowTrailingCommas = true };
                    var json = await JsonDocument.ParseAsync(stream, options).ConfigureAwait(false);
                    
                    if (!response.IsSuccessStatusCode)
                    {
                        throw new ApiException()
                        {
                            Content = json.RootElement.ToString(),
                            StatusCode = status.ToString(),
                            value = (int)status,
                        };
                    }
                    return json;
                }
            }
        }

   public static async Task<test> GetData(string id, CancellationToken ct)
    {
        string API = $"https://www.test.com/api/videos/{id}";

        using (var root = await MyClientHelper.ParseJsonData(API, ct))
        {
            var json = root.RootElement;

            //here i can access the root and dispose after

            return new test()
            {
                /////
            }
        }
    }

这就是 using 的工作方式。当您离开 using 子句时,该对象将被释放。那是故意的。

所以请考虑您的代码:

using (var json = await JsonDocument.ParseAsync(stream).ConfigureAwait(false))
        {
            if (!response.IsSuccessStatusCode)
            {
                throw new ApiException()
                {
                    Content = json.RootElement.ToString(),
                    StatusCode = status.ToString(),
                    value = (int)status,
                };
            }
            return json; <------ the moment you return it you also dispose it
        }

因此,当您尝试从外部访问它时,您会收到错误消息:

    var json = await ParseStream(stream, response);
    // here your object is already disposed
    return json.RootElement;

解决方案:在存在解析函数之前,return 你的json。 JsonDocument 对象不应在 using 子句之外使用。

作为解决方法,您不应忽略处理对象:https://docs.microsoft.com/en-us/dotnet/api/system.text.json.jsondocument?view=netcore-3.1

Failure to properly dispose this object will result in the memory not being returned to the pool, which will increase GC impact across various parts of the framework.

幸运的是,有 Clone() 方法。所以而不是:

using JsonDocument doc = JsonDocument.Parse(jsonString);
return doc; // or return doc.RootElement;`

你可以这样做:

using JsonDocument doc = JsonDocument.Parse(jsonString);
var root = doc.RootElement.Clone();
return root;

"获取可以在原始 JsonDocument 的生命周期之后安全存储的 JsonElement。" https://docs.microsoft.com/en-us/dotnet/api/system.text.json.jsonelement.clone?view=net-5.0