Stop Hand-Rolling JSON Lines With WriteLine. .NET 11 Writes Them for You
TL;DR: JsonSerializer.SerializeAsyncEnumerable(stream, items, topLevelValues: true) writes canonical JSON Lines: one compact value per line, LF after every one. No more WriteLine(JsonSerializer.Serialize(x)) loops. This ships in .NET 11, which is at Release Candidate 2 with a go-live license, but is not GA yet. Everything below was run on the .NET 11 RC 1 SDK. The loop everybody has written JSON…
JSON Lines (JSONL) is a popular format for log files, data exports, and appending data without rewriting the entire file. Previously, System.Text.Json in .NET required hand-rolled loops to write JSONL files. However, with .NET 11, this process has become much simpler.
.NET 11 introduces four new overloads for JsonSerializer.SerializeAsyncEnumerable: one that writes to a Stream or PipeWriter and another with a bool parameter called topLevelValues. When topLevelValues is set to false (the default), the output will be one JSON array, similar to the existing SerializeAsync method. When topLevelValues is set to true, it produces JSON Lines, where each value is compactly written with a newline character after it, including the last one.
This new feature uses LF as the line separator, regardless of JsonSerializerOptions.NewLine settings, and it ignores WriteIndented. The minimal example demonstrates how to write JSONL data using these new methods:
```csharp
using System.Text.Json;
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
await using var file = File.Create("orders.jsonl");
await JsonSerializer.SerializeAsyncEnumerable(file, LoadOrders(), topLevelValues: true, options: options);
```
The provided LoadOrders function generates two sample Order records, which are then written to a file in JSONL format. When compared to the hand-rolled loop using WriteIndented = true, the new JSONL mode results in a much smaller output file (78 bytes) with only two lines and no carriage returns.
Reading JSONL files back in is straightforward using the existing DeserializeAsyncEnumerable method:
```csharp
await foreach (var order in JsonSerializer.DeserializeAsyncEnumerable<Order>(stream, topLevelValues: true, options: options))
{
Console.WriteLine(order);
}
```
Both orders are successfully deserialized and displayed intact. The PipeWriter overload of the new SerializeAsyncEnumerable methods is particularly useful for ASP.NET Core applications, where Response.BodyWriter is already a PipeWriter. This allows developers to create a streaming JSONL endpoint:
```csharp
app.MapGet("/orders.jsonl", async (HttpContext ctx, CancellationToken ct) =>
{
ctx.Response.ContentType = "application/jsonl";
await JsonSerializer.SerializeAsyncEnumerable(ctx.Response.BodyWriter, LoadOrders(ct), AppJson.Default.Order, topLevelValues: true, cancellationToken: ct);
});
```
This example demonstrates a JSONL endpoint that streams orders as they come from a slow source, making it easy to integrate with a REST API.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.