using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.IO.Compression;
using System.Threading.Tasks;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Text.Json.Nodes;
public static class JsonExtensions
public static readonly RecyclableMemoryStreamManager MemoryStreamManager = new RecyclableMemoryStreamManager();
const int BufferSize = 16384;
public static async Task<byte []> CopyAndAddToCompressedByteArrayAsync<TItem>(byte [] input, IEnumerable<TItem> newItems, JsonSerializerOptions? options = default)
using var inputStream = new MemoryStream(input);
using var outputStream = MemoryStreamManager.GetStream();
await CopyAndAddToCompressedStreamAsync(inputStream, outputStream, newItems, options);
return outputStream.ToArray();
public static async Task CopyAndAddToCompressedFileAsync<TItem>(string inputPath, string outputPath, IEnumerable<TItem> newItems, JsonSerializerOptions? options = default)
await using var input = File.OpenRead(inputPath);
await using var output = File.OpenWrite(outputPath);
await CopyAndAddToCompressedStreamAsync(input, output, newItems, options);
public static async Task CopyAndAddToCompressedStreamAsync<TItem>(Stream input, Stream output, IEnumerable<TItem> newItems, JsonSerializerOptions? options = default)
options ??= JsonSerializerOptions.Default;
await using var inputDecompressor = new GZipStream(input, CompressionMode.Decompress, leaveOpen : true);
await using var outputCompressor = new GZipStream(output, CompressionMode.Compress, leaveOpen : true);
await using var outputBuffer = new BufferedStream(outputCompressor, BufferSize);
await using var writer = new Utf8JsonWriter(outputBuffer, new() { Indented = options.WriteIndented, Encoder = options.Encoder });
writer.WriteStartArray();
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<TItem>(inputDecompressor, options))
JsonSerializer.Serialize(writer, item, options);
foreach (var item in newItems)
JsonSerializer.Serialize(writer, item, options);
public static async Task<byte []> SerializeToCompressedByteArrayAsync<TValue>(TValue value, JsonSerializerOptions? options = default)
using var output = JsonExtensions.MemoryStreamManager.GetStream();
await JsonExtensions.SerializeToCompressedStreamAsync(output, value, options);
public static async Task SerializeToCompressedFileAsync<TValue>(string path, TValue value, JsonSerializerOptions? options = default)
await using var output = File.OpenWrite(path);
await SerializeToCompressedStreamAsync(output, value, options);
public static async Task SerializeToCompressedStreamAsync<TValue>(Stream utf8Json, TValue value, JsonSerializerOptions? options = default)
await using var outputCompressor = new GZipStream(utf8Json, CompressionMode.Compress, leaveOpen : true);
await using var outputBuffer = new BufferedStream(outputCompressor, BufferSize);
await JsonSerializer.SerializeAsync<TValue>(outputBuffer, value, options);
public static class FileExtensions
static readonly UTF8Encoding UTF8 = new UTF8Encoding(false, false);
public static async Task<string> ReadAllTextCompressedAsync(string path)
await using var input = File.OpenRead(path);
await using (var inputDecompressor = new GZipStream(input, CompressionMode.Decompress))
using (var textReader = new StreamReader(inputDecompressor, Encoding.UTF8, leaveOpen : true))
return await textReader.ReadToEndAsync();
public static async Task WriteAllTextCompressedAsync(string path, string text, bool bestCompression = false)
await using var output = File.OpenWrite(path);
await using (var outputCompressor = new GZipStream(output, CompressionMode.Compress))
outputCompressor.Write(UTF8.GetBytes(text));
using (var textWriter = new StreamWriter(outputCompressor, Encoding.UTF8, leaveOpen : true))
await textWriter.WriteAsync(text);
public record SipTraceRecord(string name, string value);
public static async Task Test()
Console.WriteLine("Testing byte streaming");
Console.WriteLine("\nTesting file streaming");
static async Task TestFiles()
var filePath = "Question79221507.json";
List<SipTraceRecord> initialList = Enumerable.Range(0, 10).Select(i => new SipTraceRecord("Name " + i, "Value " + i)).ToList();
var options = new JsonSerializerOptions
await JsonExtensions.SerializeToCompressedFileAsync(filePath, initialList, options);
List<SipTraceRecord> addList = new(Enumerable.Range(10, 100000).Select(i => new SipTraceRecord("Added " + i, "Added " + i)));
var tempPath = Path.GetTempFileName();
await JsonExtensions.CopyAndAddToCompressedFileAsync(filePath, tempPath, addList, options);
File.Move(tempPath, filePath, true);
var text = await FileExtensions.ReadAllTextCompressedAsync(filePath);
await FileExtensions.WriteAllTextCompressedAsync(tempPath, text, true);
Console.WriteLine(" Streaming compressed file length: {0}", new System.IO.FileInfo(filePath).Length);
Console.WriteLine(" Non-streaming compressed file length: {0}", new System.IO.FileInfo(tempPath).Length);
static async Task TestByteArrays()
List<SipTraceRecord> initialList = Enumerable.Range(0, 10).Select(i => new SipTraceRecord("Byte Array Name " + i, "Value " + i)).ToList();
var options = new JsonSerializerOptions
var initialBytes = await JsonExtensions.SerializeToCompressedByteArrayAsync(initialList, options);
List<SipTraceRecord> list = new(Enumerable.Range(10, 10).Select(i => new SipTraceRecord("Byte Array Name " + i, "Added " + i)));
var appendedBytes = await JsonExtensions.CopyAndAddToCompressedByteArrayAsync(initialBytes, list, options);
using (var input = new MemoryStream(appendedBytes))
using (var inputDecompressor = new GZipStream(input, CompressionMode.Decompress))
using (var textReader = new StreamReader(inputDecompressor, Encoding.UTF8, leaveOpen : true))
var text = await textReader.ReadToEndAsync();
public static async Task Main()
Console.WriteLine("Environment version: {0} ({1}), {2}", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , Environment.Version, Environment.OSVersion);
Console.WriteLine("System.Text.Json version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");