Неполный анализ JsonDocument с большими полезными нагрузками - PullRequest
0 голосов
/ 05 августа 2020

Итак, у меня есть HttpClient, который пытается получить любую форму данных JSON с конечной точки. Раньше я использовал Newtonsoft. Json, чтобы легко добиться этого, но после переноса всех функций в STJ я начал замечать неправильный синтаксический анализ.

Проверенные платформы: macOS и Linux (Google Kubernetes Engine )

Framework:. NET Core 3.1 LTS

На скриншотах кода ниже показан API, который возвращает массив JSON. Я просто транслирую его, загружаю в JsonDocument, а затем пытаюсь заглянуть в него. Ничего не выходит, как ожидалось. Код ниже предоставляется вместе с результатами шага отладки var.

using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Web;
using System.Xml;

namespace HttpCallDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using (var httpClient = new HttpClient())
            {
                // FLUSH
                httpClient.DefaultRequestHeaders.Clear();
                httpClient.MaxResponseContentBufferSize = 4096;
                string body = string.Empty, customMediaType = string.Empty; // For POST/PUT

                // Setup the url
                var uri = new UriBuilder("https://api-pub.bitfinex.com/v2/tickers?symbols=ALL");
                uri.Port = -1;

                // Pull in the payload
                var requestPayload = new HttpRequestMessage(HttpMethod.Get, uri.ToString());
                HttpResponseMessage responsePayload;

                responsePayload = await httpClient.SendAsync(requestPayload,
                    HttpCompletionOption.ResponseHeadersRead);

                var byteArr = await responsePayload.Content.ReadAsByteArrayAsync();
                if (byteArr.LongCount() > 4194304) // 4MB
                    return; // Too big.

                // Pull the content
                var contentFromBytes = Encoding.Default.GetString(byteArr);
                JsonDocument payload;

                switch (responsePayload.StatusCode)
                {
                    case HttpStatusCode.OK:
                        // Return the payload distinctively
                        payload = JsonDocument.Parse(contentFromBytes);

#if DEBUG
                        var testJsonRes = Encoding.UTF8.GetString(
                            Utf8Json.JsonSerializer.Serialize(payload.RootElement));
                        // var testRawRes = contentStream.read
                        var testJsonResEl = payload.RootElement.GetRawText();
#endif
                        break;
                    default:
                        throw new InvalidDataException("Invalid HTTP response.");
                }
            }
        }
    }
}

Просто выполните приведенный выше минимальный код, заметьте, что полезная нагрузка отличается от исходной после синтаксического анализа? Я уверен, что с опциями STJ что-то не так. Похоже, мы должны оптимизировать или явно определить его пределы, чтобы он мог обрабатывать эту JSON полезную нагрузку.

Initial code from HttpClient

enter image description here

Diving deeper into the debug content made things even weirder. When the HttpClient obtains the payload, reads it to a string, it gives me the entire JSON string as is. However, once we attempt to parse it into a JsonDocument and the further invoking RootElement.Clone(), we'll end up with a JsonElement with much lesser data and while carrying an invalid JSON struct (Below).

ValueKind = Array : "[["tBTCUSD",11418,70.31212518,11419,161.93475693,258.02141213,0.0231,11418,2980.0289306,11438,11003],["tLTCUSD",58.919,2236.00823543,58.95,2884.6718013699997,1.258,0.0218,58.998,63147.48344762,59.261,56.334],["tLTCBTC",0.0051609,962.80334198,0.005166,1170.07399991,-0.000012,-0.0023,0.0051609,4178.13148459,0.0051852,0.0051],["tETHUSD",396.54,336.52151165,396.55,384.37623341,8.26964946,0.0213,396.50930256,69499.5382821,397.77,380.5],["tETHBTC",0.034731,166.67781664000003,0.034751,356.03450125999996,-0.000054,-0.0016,0.034747,5855.04978836,0.035109,0.0343],["tETCBTC",0.00063087,15536.813429530002,0.00063197,16238.600279749999,-0.00000838,-0.0131,0.00063085,73137.62192801,0.00064135,0.00062819],["tETCUSD",7.2059,9527.40221867,7.2176,8805.54677899,0.0517,0.0072,7.2203,49618.78868196,7.2263,7],["tRRTUSD",0.057476,33577.52064154,0.058614,20946.501210000002,0.023114,0.6511,0.058614,210741.23592011,0.06443,0.0355],["tZECUSD",88.131,821.28048322,88.332,880.37484662,5.925,0.0

And of course, attempting to read its contents would result in:

System.InvalidOperationException: Operation is not valid due to the current state of the object.
   at System.Text.Json.JsonElement.get_Item(Int32 index)
   at Nozomi.Preprocessing.Abstracts.BaseProcessingService`1.ProcessIdentifier(JsonElement jsonDoc, String identifier) in /Users/nicholaschen/Projects/nozomi/Nozomi.Infra.Preprocessing/Abstracts/BaseProcessingService.cs:line 255

Here's proof that there is a proper 38KBs worth of data coming in from the endpoint.

введите описание изображения здесь

ОБНОВЛЕНИЕ

Дальнейшее тестирование с этим

                                    if (payload.RootElement.ValueKind.Equals(JsonValueKind.Array))
                                    {
                                        string testJsonArr;
                                        testJsonArr = Encoding.UTF8.GetString(
                                            Utf8Json.JsonSerializer.Serialize(
                                                payload.RootElement.EnumerateArray()));
                                    }

показывает, что больший массив массивов (более 9 элементов, каждый с 11 элементами ) приведет к неполной структуре JSON, что вызовет проблему, с которой я столкнулся.

1 Ответ

0 голосов
/ 06 августа 2020

Для тех, кто работает с JsonDocument и JsonElement, обратите внимание, что переменные отладки шага не точны. Не рекомендуется проверять переменные во время выполнения, так как они не отображаются полностью. создаст полный набор данных. Я настоятельно рекомендую вам обернуть сериализаторы для отладки в препроцессор DEBUG, чтобы убедиться, что эти избыточные строки в конечном итоге не будут выполнены вне разработки.

Для взаимодействия с этими объектами убедитесь, что вы используете .clone () всякий раз, когда вы может предотвратить утилизацию и убедиться, что вы обращаетесь к RootElement, а затем переходите к нему, прежде чем просматривать его значение в пошаговом режиме отладки , потому что большие значения не будут отображаться.

...