Dodo.Json.References
1.0.1
Prefix Reserved
dotnet add package Dodo.Json.References --version 1.0.1
NuGet\Install-Package Dodo.Json.References -Version 1.0.1
<PackageReference Include="Dodo.Json.References" Version="1.0.1" />
<PackageVersion Include="Dodo.Json.References" Version="1.0.1" />
<PackageReference Include="Dodo.Json.References" />
paket add Dodo.Json.References --version 1.0.1
#r "nuget: Dodo.Json.References, 1.0.1"
#:package Dodo.Json.References@1.0.1
#addin nuget:?package=Dodo.Json.References&version=1.0.1
#tool nuget:?package=Dodo.Json.References&version=1.0.1
Dodo.Json.References
High-performance System.Text.Json reference serialization: a streaming $id/$ref → JSON Pointer
transformer plus pooled (options + reference resolver) leases for large documents.
What it does
ReferenceHandler.Preserve emits opaque sequential ids:
{ "$id": "1", "items": [ { "$id": "2", "name": "x" }, { "$ref": "2" } ] }
JsonReferenceTransformer rewrites them to RFC 6901 JSON Pointers — the object's real position —
and drops unreferenced $ids, in a two-pass streaming transform with no JsonNode DOM:
{ "items": [ { "$id": "#/items/0", "name": "x" }, { "$ref": "#/items/0" } ] }
Usage
// One instance per (options, type) pair — reuse it (static field or per cache-key prefix).
private static readonly PooledReferenceSerializer<MenuModel> Serializer = new(jsonOptions);
await Serializer.SerializeWithPointers(model, httpResponse.BodyWriter, ct); // PipeWriter path
await Serializer.SerializeWithPointers(model, stream, ct); // Stream path
private static readonly PooledReferenceDeserializer<MenuModel> Deserializer = new(jsonOptions);
var model = await Deserializer.Deserialize(stream, ct);
Leases pool a warm JsonTypeInfo<T> graph and a payload-sized reference resolver per concurrent
operation; base options are snapshotted at construction and the first lease is built eagerly.
Contracts
- Metadata detection is name-based (
$id/$ref/$values); reference ids must not require JSON escaping; the encoder must not escape/or~in property names (default andUnsafeRelaxedJsonEscapingnever do). - Input must be
ReferenceHandler.Preserve-shaped: a$refnever precedes its$id(STJ always writes them in that order). Forward references pass through verbatim, untransformed. PooledReferenceHandler/PoolingReferenceResolverare single-operation: reset between documents or use a fresh instance; the pooled serializer/deserializer enforce this via leases.
Performance
Measured on a 6.6 MB production menu document (31,488 reference-tracked objects, Apple M4, .NET 10) against the JsonNode-walking baseline: ~5.9× faster, ~5.7× less allocation (per document: 104 ms / 113 MB → 17.4 ms / 19.9 MB via the PipeWriter path). Output is byte-identical to the baseline on array-shaped graphs.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.ObjectPool (>= 10.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.1 | 167 | 7/15/2026 |
| 1.0.0 | 158 | 7/15/2026 |
| 1.0.0-rc.1 | 54 | 7/15/2026 |