Skip to content

Recipes

m4bwav edited this page Sep 30, 2026 · 4 revisions

Working patterns, each with the output it gives. The outputs shown were produced with 3.0.2.

Format a file to disk

Read the input as text and stream the output, so a large document never exists twice in memory:

using System.IO;
using JsonPrettyPrinterPlus;

var printer = new JsonPrettyPrinter();
var json = File.ReadAllText("data.min.json");
using (var output = File.CreateText("data.json"))
{
    printer.PrettyPrint(json, output);
    output.WriteLine();   // a final line break, which the printer does not write
}

File.ReadAllText removes a byte order mark and File.CreateText writes UTF-8 without one. To format in place, write to a temporary file and move it over the original.

A command line filter

A .NET 10 file-based app that reads standard input and writes standard output:

#:package JsonPrettyPrinter@3.0.2
using System;
using JsonPrettyPrinterPlus;

var printer = new JsonPrettyPrinter();
printer.PrettyPrint(Console.In.ReadToEnd(), Console.Out);
Console.Out.WriteLine();
type data.min.json | dotnet run pretty.cs

(cat on Linux and macOS.) Run dotnet build pretty.cs once and then dotnet run pretty.cs --no-build to skip the build check on each call.

Log an HTTP response body

var body = await response.Content.ReadAsStringAsync();
if (logger.IsEnabled(LogLevel.Debug))
    logger.LogDebug("Response from {Url}:{NewLine}{Body}", url, Environment.NewLine, body.PrettyPrintJson());

The IsEnabled guard keeps the formatting off the hot path when debug logging is off. A body that is not JSON (an HTML error page, say) still comes back as text, indented at every bracket it happens to contain. Only a closing bracket with no opener throws, so catch FormatException if the body could be anything.

Validate first, then print

using System.Text.Json;

static string PrettyOrError(string text)
{
    try
    {
        using var _ = JsonDocument.Parse(text);
        return text.PrettyPrintJson();
    }
    catch (JsonException e)
    {
        return "Not JSON: " + e.Message;
    }
}

JsonDocument.Parse is strict: {"a":1,} fails with The JSON object contains a trailing comma at the end which is not supported in this mode. Change the reader options. LineNumber: 0 | BytePositionInLine: 7. To accept trailing commas and comments, pass a JsonDocumentOptions with AllowTrailingCommas and CommentHandling = JsonCommentHandling.Skip, and then pretty print the parsed node's ToJsonString() rather than the original text, or the comments come through (Not a validator).

Comparing JSON in tests

Two documents that differ only in whitespace pretty print to the same text, so an assertion can compare the formatted forms and show a readable diff when it fails:

Assert.That(actual.PrettyPrintJson(), Is.EqualTo(expected.PrettyPrintJson()));

This is a text comparison: key order, number formatting (1.0 against 1) and escapes (\u00e9 against é) all count as differences. For a semantic comparison use JsonNode.DeepEquals(JsonNode.Parse(actual), JsonNode.Parse(expected)), which ignores whitespace and key order, and pretty print only for the failure message.

Snapshot files are easier since 3.0 because the line ending is a line feed on every OS. If a snapshot goes through Git with autocrlf on, store it with eol=lf in .gitattributes or normalise line endings before comparing.

One readable line

For a log line or a test name, put everything on one line with spaces instead of line breaks:

var oneLine = new JsonPrettyPrintOptions { IndentSize = 0, NewLine = " " };
"""{"a":1,"b":[1,2],"c":{}}""".PrettyPrintJson(oneLine);
// { "a": 1, "b": [ 1, 2 ], "c": {} }

Minify

The printer only adds whitespace. To remove it, parse and write back:

using System.Text.Json.Nodes;

var compact = JsonNode.Parse("""{ "a" : [ 1 , 2 ] }""")!.ToJsonString();   // {"a":[1,2]}

An ASP.NET Core endpoint

A minimal API that returns whatever it is sent, formatted:

app.MapPost("/pretty", async (HttpRequest request) =>
{
    using var reader = new StreamReader(request.Body);
    var json = await reader.ReadToEndAsync();
    try
    {
        return Results.Text(json.PrettyPrintJson(), "application/json");
    }
    catch (FormatException e)
    {
        return Results.BadRequest(e.Message);
    }
});

Limit the request body size ([RequestSizeLimit] or the Kestrel option) before exposing this to the internet, because the input has to fit in memory. PrettyPrintJson() is safe to call from concurrent requests.

A reusable printer with your own options

PrettyPrintJson(options) creates a printer per call. In a hot path with fixed options, keep one per thread:

using System.Threading;

private static readonly ThreadLocal<JsonPrettyPrinter> Printer =
    new(() => new JsonPrettyPrinter(new JsonPrettyPrintOptions { IndentSize = 2 }));

public static string Pretty(string json) => Printer.Value!.PrettyPrint(json);

Other serialisers

Any serialiser's compact output can go through the printer.

Newtonsoft.Json:

var pretty = JsonConvert.SerializeObject(order).PrettyPrintJson();

(Formatting.Indented gives Newtonsoft's own layout instead: two spaces and Environment.NewLine.)

PowerShell, after loading the assembly as Getting started shows:

$compact = [ordered]@{ name = 'Ada'; tags = @('math') } | ConvertTo-Json -Compress
[JsonPrettyPrinterPlus.PrettyPrinterExtensions]::PrettyPrintJson($compact)
{
    "name": "Ada",
    "tags": [
        "math"
    ]
}

System.Text.Json's own objects have compact text forms too:

var fromNode = node.ToJsonString().PrettyPrintJson();
var fromElement = element.GetRawText().PrettyPrintJson();

Console output on Windows

Set Console.OutputEncoding = System.Text.Encoding.UTF8 before writing text with characters outside ASCII. The printer keeps them as they are, and the default console code page may not show them.

Clone this wiki locally