Можно десериализовать одну полезную нагрузку JSON из списка строк, представляющих куски JSON, путем построения ReadOnlySequence<byte>
из списка, а затем построения Utf8JsonReader
из последовательности, и, наконец, десериализацию с использованием считывателя через JsonSerializer.Deserialize<TValue>(Utf8JsonReader, JsonSerializerOptions)
.
Ниже приведена минимальная реализация:
public static partial class JsonExtensions
{
public static TValue Deserialize<TValue>(IEnumerable<string> buffers, JsonSerializerOptions options = null)
{
return Deserialize<TValue>(ToByteArrayChunks(buffers));
}
public static TValue Deserialize<TValue>(IEnumerable<byte []> buffers, JsonSerializerOptions options = null)
{
var sequence = ReadOnlySequenceFactory.Create(buffers);
var reader = new Utf8JsonReader(sequence, options.GetReaderOptions());
return JsonSerializer.Deserialize<TValue>(ref reader, options);
}
public static JsonReaderOptions GetReaderOptions(this JsonSerializerOptions options)
{
if (options == null)
return new JsonReaderOptions();
else
return new JsonReaderOptions
{
AllowTrailingCommas = options.AllowTrailingCommas,
CommentHandling = options.ReadCommentHandling,
MaxDepth = options.MaxDepth
};
}
static readonly Encoding encoding = new UTF8Encoding(false);
static IEnumerable<byte []> ToByteArrayChunks(IEnumerable<string> buffers)
{
// By using an encoder we can handle the situation in which surrogate pairs enbedded in JSON string literals
// are split between chunks.
var encoder = encoding.GetEncoder();
foreach (var s in buffers)
{
ReadOnlySpan<char> charSpan = s;
var count = encoder.GetByteCount(charSpan, false);
var bytes = new byte[count];
Span<byte> byteSpan = bytes;
encoder.GetBytes(charSpan, byteSpan, false);
yield return bytes;
}
}
}
public static class ReadOnlySequenceFactory
{
// There is no public concrete implementation of ReadOnlySequenceSegment<T> so we must create one ourselves.
// This is modeled on https://github.com/dotnet/runtime/blob/master/src/libraries/System.Text.Json/tests/BufferFactory.cs
// by https://github.com/ahsonkhan
class ReadOnlyMemorySegment<T> : ReadOnlySequenceSegment<T>
{
public static ReadOnlySequence<T> Create(IEnumerable<ReadOnlyMemory<T>> buffers)
{
ReadOnlyMemorySegment<T> first = null;
ReadOnlyMemorySegment<T> current = null;
foreach (var buffer in buffers)
{
var next = new ReadOnlyMemorySegment<T> { Memory = buffer };
if (first == null)
{
first = next;
}
else
{
current.Next = next;
next.RunningIndex = current.RunningIndex + current.Memory.Length;
}
current = next;
}
if (first == null)
{
first = current = new ReadOnlyMemorySegment<T>();
}
return new ReadOnlySequence<T>(first, 0, current, current.Memory.Length);
}
}
public static ReadOnlySequence<T> Create<T>(IEnumerable<T []> buffers)
{
return ReadOnlyMemorySegment<T>.Create(buffers.Select(b => new ReadOnlyMemory<T>(b)));
}
}
Примечания:
A ReadOnlySequence<T>
создается из связанного списка ReadOnlySequenceSegment<T>
объектов - но этот тип является абстрактным и NET Core 3.1, похоже, не включает конкретная публикация c реализации. Я смоделировал реализацию выше для этого от Ahson Khan .
JsonSerializer
, предназначенного для десериализации из кодированных байтовых последовательностей UTF-8 а не из строк или символьных массивов, поэтому, если вы сможете заставить свой уровень доступа к базе данных возвращать список байтовых массивов UTF-8, а не строк, вы получите более высокую производительность и избежите этапа кодирования каждого фрагмента строки в байты.
Если это невозможно, и ваш ввод определенно является длинным списком маленьких sh строк (2033 символа), возможно, стоит изучить использование пула памяти или массива для выделения необходимых последовательностей байтов UTF-8.
Несмотря на то, что при этом подходе не требуется выделять одну огромную string
или byte []
, вся полезная нагрузка JSON, тем не менее, загружается в память все сразу как последовательность кусков. Таким образом, это не настоящее потоковое решение.
Если вы заинтересованы в истинном потоковом решении и можете получить доступ к вашим JSON данным напрямую как Stream
, вы можете посмотреть на этот ответ до Анализ файла JSON с. NET core 3.0 / System.text. Json с mto sh.
Демонстрационная скрипка здесь .