Jakar.Json 10.2.0

dotnet add package Jakar.Json --version 10.2.0
                    
NuGet\Install-Package Jakar.Json -Version 10.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Jakar.Json" Version="10.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Jakar.Json" Version="10.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Jakar.Json" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Jakar.Json --version 10.2.0
                    
#r "nuget: Jakar.Json, 10.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Jakar.Json@10.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Jakar.Json&version=10.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Jakar.Json&version=10.2.0
                    
Install as a Cake Tool

Jakar.Json

A reflection-free, allocation-free JSON serializer. [JsonModel] source-generates readers and writers for UTF-16 and UTF-8 that implement ISpanFormattable / ISpanParsable<T> (and the UTF-8 versions). The package also has a zero-allocation JSON tape and a Newtonsoft-style DOM. The serializer doesn't use System.Text.Json. The design is in SPEC.md.

The package also contains Native AOT friendly System.Text.Json models ([JsonModel], formerly the Jakar.SystemTextJson package; see below). One attribute serves both: [JsonModel] without a context is Jakar.Json only; with a System.Text.Json context it implements IJsonModel<T> with whichever serializer is faster for the type.

using Jakar.Extensions;   // [JsonModel] (it keeps the namespace it had in Jakar.SystemTextJson)
using Jakar.Json;         // JsonMember, JsonNaming, JsonCodec, ...

[JsonModel(Naming = JsonNaming.CamelCase)]   // no context: Jakar.Json only
public sealed partial record Invoice( Guid Id, decimal Total )
{
    public required List<LineItem> Lines { get; init; }

    [JsonMember(Name = "memo", NullValues = JsonNullValues.Omit)]
    public string? Notes { get; init; }
}

string  json    = invoice.ToJson();                // {"id" : "…","total" : 1.10,"lines" : [...]}
byte[]  utf8    = invoice.ToJsonUtf8();
Invoice back    = Invoice.FromJson(json);          // or FromJson(ReadOnlySpan<byte>), FromJson(Stream), FromJsonAsync(...)
bool    ok      = Invoice.TryFromJson(json, out Invoice? maybe);
bool    written = invoice.TryFormat(buffer, out int chars, default, null);   // ISpanFormattable: no allocation
T       parsed  = T.Parse(text, null);                                       // ISpanParsable<T> in generic code

What you get

  • No reflection. Everything is generated at compile time: member access, construction, enum names, polymorphic dispatch. Converters are static interfaces (IJsonConverter<T>), so generic code is specialized by the JIT. It publishes with Native AOT and full trimming with no warnings.
  • No incidental allocations.
    • Writing to a span, an IBufferWriter or a Stream allocates nothing.
    • ToJson() allocates only the result string.
    • Reading allocates only the result graph. Collections are allocated once, at their exact size.
    • The test suite checks these budgets exactly (AllocationTests.cs).
  • Idempotent.
    • The same value always produces the same bytes: on any machine, OS, culture or thread, and whatever order its dictionaries and sets were filled in.
    • Reading and re-writing any document reaches one canonical form in a single pass.
    • Canonical numbers: the shortest round-trippable form, an exponent like 1e+20, and decimal keeps its scale.
  • Strict and bounded. Reading follows RFC 8259 by default. Depth is limited, and nesting is tracked without recursion, so a deeply nested document can't overflow the stack. Invalid UTF-8 and unpaired surrogates are rejected. Errors give the position, line, column and JSON path ($.lines[3].price).
  • Both encodings natively. JsonWriter (UTF-16, on ValueStringBuilder), JsonUtf8Writer (UTF-8, on ValueUtf8Builder, or streaming to a Stream), and JsonReader<char> / JsonReader<byte> (on ValueSpanReader<T>).

Settings

Settings resolve from the nearest level: [JsonMember] on the member, then [JsonModel] on the type, then the type's System.Text.Json context (mirrored, below), then [assembly: JsonDefaults], then the built-in default. They're compiled into the generated code. A nested type always uses its own settings, so a type's JSON doesn't depend on what contains it.

Setting Default Options
Naming / EnumNaming AsDeclared CamelCase, PascalCase, SnakeCaseLower/Upper, KebabCaseLower/Upper
NameMatching Exact OrdinalIgnoreCase
NullValues / DefaultValues Write Omit
UnknownMembers Skip Error, Capture (into a [JsonMember(ExtensionData = true)] member)
DuplicateMembers Error LastWins
Enums Name Number (reading accepts names and numbers either way)
NumbersFromStrings Disallow Allow
LargeIntegers Number String (beyond ±2^53, for JavaScript)
NonFiniteFloats Error AsString ("NaN", "Infinity")
LocalDateTimes ConvertToUtc WriteOffset, Error
UnorderedCollections Sorted Enumeration
Escaping Minimal AsciiOnly, HtmlSafe
Indented, IndentChar, IndentSize off, tab, 1 —
AllowComments, AllowTrailingCommas off —
MaxDepth 64 up to 1024
GenerateToString on —

Output is compact unless indented, with " : " between names and values: {"id" : 1,"name" : "x"}. Indentation is one tab per level by default.

Members, constructors and types

  • Which members. Public properties and fields are included, plus anything marked [JsonMember]. Members are ordered by Order, then base types first, then declaration order.
  • Construction. The generator uses, in order: the constructor marked [JsonConstructor], the primary constructor, the single public constructor, or the parameterless one. Parameters bind to members by name. Every other member is set only when it's present in the JSON, so property initializers survive a missing member. Init-only members are set through [UnsafeAccessor], which is resolved at compile time.
  • Required members. required members, constructor parameters without defaults, and [JsonMember(Required = true)] members must be present when reading.
  • Supported types:
    • all primitives, including Int128, UInt128, BigInteger and Half;
    • string, char, Guid, DateTime, DateTimeOffset, DateOnly, TimeOnly, TimeSpan, Uri and Version;
    • enums (with [Flags] written as "A, B") and Nullable<T>;
    • other [JsonModel] types Jakar.Json implements, and any ISpanFormattable + ISpanParsable<T> type;
    • arrays, List<T>, the list and collection interfaces, the immutable, frozen, sorted and hash-based sets, Queue<T> and Stack<T>;
    • every dictionary type, keyed by string, an enum, or any parsable type;
    • any IJsonCollection<TSelf, T>: a collection that writes its own elements and is built from a span in one call (the Jakar.Extensions observable collections write their unfiltered contents this way);
    • JNode and its subtypes, and System.Text.Json's JsonElement.
  • Custom converters. [JsonMember(Converter = typeof(MyConverter))], where the converter is a struct implementing IJsonConverter<T>.
  • Polymorphism. [JsonDerived(typeof(Dog), "dog")] on the base type. The discriminator ("$type" by default) is written first, and when reading it may appear anywhere in the object.
  • Generics. Type parameters work when constrained to IJsonSerializable<T>, or to ISpanFormattable + ISpanParsable<T>.
  • Migrating from System.Text.Json. These attributes are honored: JsonPropertyName, JsonPropertyOrder, JsonIgnore (including its conditions), JsonRequired, JsonInclude, JsonExtensionData, JsonConstructor and JsonStringEnumMemberName.

Helpers

JsonCodec has the generic versions of every generated helper, plus:

  • Root arrays: ToJsonArray, FromJsonArray, FromJsonList, and FromJsonArrayPooled, which returns a pooled buffer you dispose.
  • NDJSON: ReadLines<T> / ReadLinesAsync<T> and WriteLines<T>. Memory is bounded by the longest line.
  • Inputs and outputs: ReadOnlySequence<byte>, TextReader / TextWriter, appending to a ValueStringBuilder, and any buffer writer: WriteJson(value, ref writer) (UTF-8) and WriteJsonUtf16(value, ref writer) take a generic TWriter : IBufferWriter<…> by ref, so struct and ref struct writers work without boxing.
  • By Type at run time: JsonCodecRegistry.TryGet(type, out JsonTypeCodec codec), then codec.Read(...) / codec.Write(value, ref bufferWriter). For frameworks that pick a serializer per type. Each assembly's generated module initializer registers its non-generic [JsonModel] types, and lookups also resolve T[], List<T> and the common collection interfaces of them. No reflection. Jakar.Json.AspNetCore uses it for MVC and minimal APIs.

Dynamic JSON

  • JsonTape parses into a pooled index. Navigating it allocates nothing: tape.Root["lines"][3]["price"].GetDecimal(), EnumerateArray(), ValueEquals("paid"). Deserialize<T>() reads a model straight from the index.
  • JNode (JObjectNode, JArrayNode, JValueNode) is a mutable tree:
    • Indexers return null for both a missing member and JSON null. Implicit and explicit conversions cover the common types.
    • Parsed numbers keep their original text.
    • DeepClone, DeepEquals, Detach and Path are available.
    • ToModel<T>() and FromModel<T>() convert without going through text.

System.Text.Json models ([JsonModel] with a context)

Formerly the Jakar.SystemTextJson package. The types keep the Jakar.Extensions namespace.

using System.Text.Json.Serialization;
using Jakar.Extensions;

[JsonSerializable(typeof(Invoice))]
public sealed partial class AppJsonContext : JsonSerializerContext;

[JsonModel(typeof(AppJsonContext))]
public sealed partial class Invoice
{
    public decimal Total { get; init; }
}

Invoice invoice = Invoice.FromJson(json);   // generated, uses AppJsonContext.Default.Invoice
string  text    = invoice.ToJson();
  • [JsonModel] implements IJsonModel<TSelf> on a partial class, record or struct: the JsonTypeInfo property, FromJson / TryFromJson / FromJsonAsync, and a registration in JsonModelRegistry.
  • The type must be registered in its context ([JsonSerializable(typeof(Invoice))]). The System.Text.Json generator can't see other generators' output, so this can't be done for you; diagnostic JAKAR_JSON001 has a code fix that adds it.
  • [assembly: JsonModelContext(typeof(AppJsonContext))] sets a default context so [JsonModel] can be used without arguments.
  • Root-level JSON arrays are read and written element by element with only the element's metadata, so List<T>, T[], ImmutableArray<T>, ... never need registering: JsonModel.FromJsonList(json, Invoice.JsonTypeInfo), invoices.ToJson(), or JsonModel.FromJsonArrayPooled(...) for a pooled buffer (RentedArray<T>).
  • SerializeAsStringJsonConverter<T> serializes any ISpanParsable<T> + ISpanFormattable type as a JSON string.
  • The faster serializer behind IJsonModel. When Jakar.Json supports the type, FromJson / TryFromJson / FromJsonAsync / ToJson() / ToUtf8Json() / WriteToAsync() run on it (faster, fewer allocations) and the type also implements IJsonSerializable<T>; JsonTypeInfo stays System.Text.Json's. Otherwise the type uses System.Text.Json and JJSON016 (Info) says why. Implementation = SystemTextJson / JakarJson forces one. Failures are still JsonExceptions.
  • The context's options are mirrored into Jakar.Json's settings (naming, case-insensitivity, enums as names or numbers, indentation, ignored nulls/defaults, numbers from strings, comments, trailing commas, fields, optional constructor parameters), so both serializers' JSON means the same and each reads the other's. Only the text differs ("name" : value, minimal escaping). SPEC.md §7.7 has the full table and what makes a type fall back.
ID Severity Meaning
JAKAR_JSON001 Error The type isn't registered in its context (code fix adds [JsonSerializable(typeof(T))])
JAKAR_JSON002 Error The type, or a containing type, isn't partial
JAKAR_JSON003 Error Open generic types can't be source generated; put [JsonModel] on the closed derived type, or implement JsonTypeInfo with JsonModel.GetRequiredTypeInfo<T>()
JAKAR_JSON004 Error The context isn't a JsonSerializerContext, or none was given and there's no assembly default
JAKAR_JSON005 Warning The type hand-writes JsonTypeInfo; the generator leaves it alone
JAKAR_JSON006 Error The type lists IJsonModel<T> without [JsonModel] and doesn't implement it (code fix adds [JsonModel])
JAKAR_JSON007 Warning An IJsonModel.AdditionalData implementation lacks [JsonExtensionData], so unknown members are dropped

Diagnostics

ID Severity Meaning
JJSON001 Error The type, or a containing type, isn't partial (code fix)
JJSON002 Error A type parameter needs IJsonSerializable<T> or ISpanFormattable, ISpanParsable<T>
JJSON003 Error A member's type isn't supported
JJSON004 Error No usable constructor, or a constructor parameter matches no member
JJSON005 Error Two members have the same JSON name
JJSON006 Warning Members span several files without explicit Order (code fix freezes the order)
JJSON007 Info A generated member was already declared by hand, so it's not generated
JJSON008 Error Contradictory settings
JJSON009 Error A converter doesn't fit its member
JJSON010 — Retired (side-by-side mode, before [GenerateJson] merged into [JsonModel])
JJSON011 Info A member is written but never read (no setter, no constructor parameter)
JJSON012 Error object, delegate, pointer or ref struct member
JJSON013 Warning A setting weakens deterministic output
JJSON014 Error Invalid [JsonDerived]
JJSON015 Error IJsonSerializable<T> listed without [JsonModel] (code fix)
JJSON016 Info A context-backed [JsonModel] uses System.Text.Json: Jakar.Json can't match it (the reason is in the message)
JJSON017 Error Implementation = JakarJson, but Jakar.Json can't implement the type

Notes

  • One attribute. [JsonModel] without a context is Jakar.Json only (IJsonSerializable<T>); with one it's IJsonModel<T>, on Jakar.Json when possible. Moving a context-backed type to Jakar.Json needs no code change: fix what JJSON016 reports.
  • The only reflection-adjacent code is in the BCL collections the reader creates, such as Dictionary<TKey, TValue> with its default comparer. Jakar.Json itself makes no reflection calls, and writing to sets and dictionaries uses comparisons chosen at compile time.
  • byte[] is written as a JSON array of numbers, not base64. Use a converter if you need base64.
  • Object references aren't preserved. A cyclic graph fails at MaxDepth with DepthExceeded.
  • Settings come only from the arguments written on an attribute: a setting you don't set falls through to the next level, so the defaults in the table above apply.
  • ASP.NET Core: Jakar.Json.AspNetCore makes Jakar.Json the serializer for [JsonModel] types in MVC and minimal APIs.
  • Versions follow <.NET major>.<library major>.<library minor> (see the repository README).
Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (35)

Showing the top 5 NuGet packages that depend on Jakar.Json:

Package Downloads
Jakar.Extensions

Extensions to aid in development.

Jakar.Json.AspNetCore

Jakar.Json as ASP.NET Core's JSON serializer for [JsonModel] types: MVC input/output formatters (AddJakarJson), and for minimal APIs typed results, a request-body wrapper and an endpoint filter. Other types keep using System.Text.Json. Reflection-free.

Jakar.Json.FusionCache

Jakar.Json as FusionCache's distributed-cache serializer: [JsonModel] values (and collections of them) are written by Jakar.Json, common scalars in binary, inside a compact binary entry envelope. Other types can go to a fallback serializer. Reflection-free under Native AOT.

Jakar.Json.SystemTextJson

Jakar.Json inside System.Text.Json: a JsonConverterFactory that reads and writes [JsonModel] types (and collections of them) with Jakar.Json wherever System.Text.Json is configured - SignalR, Blazor, HttpClient, Npgsql, Dapr, Azure SDKs, Microsoft.Extensions.AI and more.

Jakar.Json.AwsLambda

Jakar.Json as an AWS Lambda serializer: [JsonModel] requests and responses are read and written by Jakar.Json (reflection-free, Native AOT friendly); other types (AWS event types) go to a fallback serializer.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.2.0 60 10/17/2026
10.1.0 220 10/16/2026

10.2.0 (breaking): buffer writers are generic and passed by ref, so struct and ref struct writers work without boxing: JsonCodec.WriteJson<T, TWriter>(in T, ref TWriter) (UTF-8), JsonCodec.WriteJsonUtf16<T, TWriter>(in T, ref TWriter), JsonCodec.WriteJsonArray<T, TWriter>(..., ref TWriter), JsonTypeCodec.Write<TWriter>(object?, ref TWriter), and the generated WriteJson<TWriter>(ref TWriter); the IBufferWriter<byte|char> overloads are gone. 10.1.0: initial release. Absorbs Jakar.SystemTextJson (IJsonModel<TSelf>, [JsonModel], JsonModel, RentedArray<T>, SerializeAsStringJsonConverter<T>; still in the Jakar.Extensions namespace, diagnostics JAKAR_JSON001-007 unchanged). One attribute, [JsonModel]: without a System.Text.Json context it's Jakar.Json only; with one it implements IJsonModel<TSelf> on Jakar.Json when that supports the type (faster, fewer allocations; the context's options mirrored), otherwise on System.Text.Json (JJSON016). [GenerateJson] and JJSON010 are gone.