NoJsonSchema 1.2.0

dotnet add package NoJsonSchema --version 1.2.0
                    
NuGet\Install-Package NoJsonSchema -Version 1.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="NoJsonSchema" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NoJsonSchema" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="NoJsonSchema" />
                    
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 NoJsonSchema --version 1.2.0
                    
#r "nuget: NoJsonSchema, 1.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 NoJsonSchema@1.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=NoJsonSchema&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=NoJsonSchema&version=1.2.0
                    
Install as a Cake Tool

NoJsonSchema

NuGet License: MIT

Generate C# types and a zero-dependency UTF-8 JSON parser/emitter from JSON Schema.

NoJsonSchema takes a JSON Schema Draft 2020-12, Draft-07, or OpenAPI 3.x document and generates C# models plus UTF-8 JSON serialization code at build time.

The generated output consists of:

  1. POCO typesclass / record / readonly record struct, your choice per type.
  2. A per-type Formatter — UTF-8 parser and emitter built on ref byte + Unsafe.Add.
  3. A namespace-wide Serializer — generic Deserialize<T> / Serialize<T> with Cache<T> dispatch (one resolve per CLR generic instantiation, then a single static-field load).
JSON Schema  ─►  NoJsonSchema  ─►  C# types + UTF-8 JSON IO  ─►  your app
                  (build-time)        (BCL-only, AOT-safe)

Zero runtime dependencies

The generated .cs files reference nothing except the BCL — no System.Text.Json, no Newtonsoft.Json, no reflection, no third-party packages of any kind. Just System, System.Buffers, System.IO, System.Runtime.CompilerServices.

That means:

  • Native AOT works without reflection roots or rd.xml entries.
  • Unity / IL2CPP can use the generated code without depending on System.Text.Json, Newtonsoft.Json, or JsonUtility.
  • Trimming (PublishTrimmed=true) is safe because the generator doesn't emit anything that survives trim analysis as un-rooted reflection.

The Core library itself (NoJsonSchema.dll) depends on System.Text.Json — but only for reading the schema document at generator time. None of that touches your shipped binary.

Why

NoJsonSchema is for schema-first projects: cases where the JSON contract is shared, published, or generated outside your C# codebase, and your C# models need to follow it.

Typical cases:

  • A public JSON Schema or OpenAPI document exists, but there is no C# SDK for it.
  • A protocol is defined from a source of truth such as TypeSpec or a shared API specification.
  • Your target cares about zero runtime dependencies: Native AOT, Unity / IL2CPP, or trimmed apps.

If your application owns the C# types and only needs a schema as an output, a code-first workflow is usually simpler.

The generated parser/emitter is also built for hot paths:

NoJsonSchema System.Text.Json source-gen
Generated code runtime deps none System.Text.Json
Deserialize (8-property DTO) 355 ns (1.63× faster) 577 ns
Serialize (same DTO) 210 ns (1.34× faster) 282 ns
Allocations on deserialize 792 B (40% less) 1328 B
Unity / IL2CPP / AOT ✅ (limited)

(Benchmark: Apple M4 / .NET 10, ShortRun. See samples/Bench/.)

API at a glance

To get a feel for the generated API, start with a schema like this (user.json):

{
  "$defs": {
    "Address": {
      "type": "object",
      "properties": {
        "city": { "type": "string" },
        "zip":  { "type": "string" }
      },
      "required": ["city", "zip"]
    },
    "User": {
      "type": "object",
      "properties": {
        "id":      { "type": "integer", "format": "int32" },
        "name":    { "type": "string" },
        "email":   { "type": "string" },
        "joined":  { "type": "string", "format": "date" },
        "address": { "$ref": "#/$defs/Address" }
      },
      "required": ["id", "name"]
    }
  }
}

Generate it with either the source generator (AdditionalFiles, shown below) or the CLI (nojsonschema generate -i user.json -o ./Generated -n MyApp.Models).

NoJsonSchema emits User.g.cs, Address.g.cs, Formatters/UserFormatter.g.cs, Formatters/AddressFormatter.g.cs, and a namespace-wide MyAppModelsSerializer.g.cs. You use it like this:

using MyApp.Models;

// The namespace-wide Serializer is the entry point.
var user = MyAppModelsSerializer.Deserialize<User>(utf8Bytes);
var bytes = MyAppModelsSerializer.SerializeToUtf8Bytes(user);

// IBufferWriter<byte> overload too (zero extra copy).
MyAppModelsSerializer.Serialize(myBufferWriter, user);

The CLI / source generator stamps a static {Namespace}Serializer class into the same namespace as the POCOs. Use that — the per-type XxxFormatter is internal-by-design (no public surface to lock in).

Installation

Choose one of three workflows:

  • Use the source generator when schemas should be compiled into your project automatically.
  • Use the CLI when you want to vendor generated files or generate many schemas at once.
  • Use the library API when you are building custom tooling.

Source generator

Use the source generator when you want NoJsonSchema to generate the API automatically during your build.

dotnet add package NoJsonSchema
dotnet add package NoJsonSchema.SourceGenerator

Add your schema to the project file as an AdditionalFiles item:

<ItemGroup>
  <AdditionalFiles Include="schemas/my-schema.json">
    <NoJsonSchemaNamespace>MyApp.Models</NoJsonSchemaNamespace>
  </AdditionalFiles>
</ItemGroup>

On the next build, the generated types and namespace-wide serializer appear under that namespace.

For the CLI or library workflows, see the sections below.

Standalone CLI (good for vendored / bulk generation)

dotnet tool install -g NoJsonSchema.Cli

nojsonschema generate -i schema.json -o ./Generated -n MyApp.Models

The CLI accepts file paths or http(s) URLs — point it at a remote schema directly:

nojsonschema generate \
  -i https://raw.githubusercontent.com/microsoft/debug-adapter-protocol/main/debugAdapterProtocol.json \
  -o ./Dap -n Dap
Usage: generate [options...] [-h|--help] [--version]

Generate C# code from a JSON Schema.

Options:
  -i, --input <string>                Path or http(s) URL of the JSON Schema. [Required]
  -o, --output <string>               Directory to write generated .cs files into. [Required]
  -n, --namespace <string>            Namespace for generated types. [Default: @"Generated"]
  --root-type <string?>               Optional root type name override. [Default: null]
  --type-style <TypeStyle>            Type style for objects (Class | Record | ReadonlyRecordStruct). [Default: Class]
  --allof-strategy <AllOfStrategy>    How to represent allOf composition (Inherit | Flatten). [Default: Inherit]
  --strict-extra                      Treat unknown JSON properties as errors.
  --value-object <string[]?>          Comma-separated list of $defs entries to emit as readonly record struct (primary-ctor) value objects. [Default: null]
  --use-required                      Emit the C# 11 'required' modifier on non-nullable required properties (otherwise '= null!' is used to suppress CS8618).
  --include-type <string[]?>          Comma-separated whitelist of $defs / components.schemas entries to generate (transitive deps included automatically). Default is everything. [Default: null]

Library (custom tooling, MSBuild target, Roslyn integration)

dotnet add package NoJsonSchema
var pipeline = new NoJsonSchema.Core.GeneratorPipeline();
var result = pipeline.Generate(File.ReadAllText("schema.json"), new GenerationOptions
{
    Namespace = "MyApp.Models",
});
foreach (var f in result.Files)
    File.WriteAllText(Path.Combine("./Generated", f.FileName), f.SourceText);

Configuration

All knobs are surfaced through three equivalent channels — MSBuild metadata on the AdditionalFiles entry (source generator), CLI flags (nojsonschema), and GenerationOptions properties (library).

MSBuild metadata CLI flag GenerationOptions Default What it does
NoJsonSchemaNamespace -n, --namespace Namespace Generated Target C# namespace.
--root-type RootTypeName (none) Optional root type name override (only useful when the schema's root is itself a single object schema).
NoJsonSchemaTypeStyle --type-style TypeStyle Class Class / Record / ReadonlyRecordStruct.
--allof-strategy AllOfStrategy Inherit How to represent allOf composition — Inherit (emit base class + derived) or Flatten (inline all properties into one type).
NoJsonSchemaStrictExtraProperties --strict-extra StrictExtraProperties false Throw NoJsonFormatException on unknown JSON properties.
NoJsonSchemaValueObjects --value-object ValueObjectTypes (empty) ;/,-separated $defs entries to emit as readonly partial record struct (primary-ctor form).
NoJsonSchemaUseRequired --use-required UseRequiredModifier false Use C# 11 required modifier on non-nullable required properties (otherwise = null!).
NoJsonSchemaIncludeTypes --include-type IncludedTypes (everything) ;/,-separated whitelist of $defs / components.schemas entries to generate (transitive deps included automatically).

Per-type metadata example

<AdditionalFiles Include="schemas/dap.json">
  <NoJsonSchemaNamespace>Dap</NoJsonSchemaNamespace>
  <NoJsonSchemaValueObjects>Checksum;Source</NoJsonSchemaValueObjects>
  <NoJsonSchemaIncludeTypes>InitializeRequest;StoppedEvent;StackTraceResponse</NoJsonSchemaIncludeTypes>
</AdditionalFiles>

CLI reference

The nojsonschema tool has two subcommands. Both accept either a local file path or an http(s) URL for --input.

nojsonschema generate

Generate C# types + UTF-8 parser/emitter from a schema.

nojsonschema generate -i <schema> -o <out-dir> [options...]
Flag Argument Default Description
-i, --input <path-or-url> required Local path or http(s) URL of the JSON Schema / OpenAPI document.
-o, --output <dir> required Directory to write generated .cs files into (subdirs are created as needed).
-n, --namespace <string> Generated Target C# namespace for the emitted types and Serializer.
--root-type <string> (none) Override the root type's name. Most schemas drive type names from $defs entries — this is for the rare case where you want to rename the top-level object.
--type-style Class \| Record \| ReadonlyRecordStruct Class How to emit object types. Class uses { get; set; }. Record is a C# record with { get; init; }. ReadonlyRecordStruct is a readonly partial record struct with a primary constructor (value-object form).
--allof-strategy Inherit \| Flatten Inherit How to represent allOf composition. Inherit emits a base class + derived class. Flatten inlines all parent properties into a single class.
--strict-extra (flag) false Throw NoJsonFormatException when the JSON payload contains a property the schema didn't declare. Otherwise unknown properties are skipped.
--value-object <csv> (empty) Comma- or semicolon-separated list of $defs entries to emit as readonly partial record struct (primary-ctor form). E.g. --value-object Color,SemVer. Per-type override of --type-style.
--use-required (flag) false Emit the C# 11 required modifier on non-nullable required properties. Without this, = null! suppresses CS8618 instead. Ships a _SetsRequiredMembersShim.g.cs polyfill for netstandard2.0/pre-net7 consumers.
--include-type <csv> (everything) Whitelist of top-level $defs / components.schemas entries to generate. Transitive dependencies are included automatically — e.g. --include-type Pet will also pull Cat, Dog if they're oneOf branches. Useful for trimming massive schemas (DAP has 192 defs; you might only need a handful).
-h, --help (flag) Print usage and exit.
Examples
# Vanilla: local schema → ./Generated under "MyApp.Models"
nojsonschema generate -i schema.json -o Generated -n MyApp.Models

# Remote schema with --include-type whitelist (trim 192-def DAP to 13 files)
nojsonschema generate \
  -i https://raw.githubusercontent.com/microsoft/debug-adapter-protocol/main/debugAdapterProtocol.json \
  -o ./Dap -n Dap \
  --include-type Capabilities,InitializeRequest,StoppedEvent

# Mix: most types as classes, but Checksum/Source as readonly record structs
nojsonschema generate -i dap.json -o ./Dap -n Dap \
  --value-object Checksum,Source \
  --use-required --strict-extra

nojsonschema lint

Parse a schema and print a summary; reports structural problems without generating code.

nojsonschema lint -i <schema>
Flag Argument Default Description
-i, --input <path-or-url> required Local path or http(s) URL of the JSON Schema / OpenAPI document.
-h, --help (flag) Print usage and exit.

Output:

$ nojsonschema lint -i https://json.schemastore.org/package.json
file:        https://json.schemastore.org/package.json
$schema:     http://json-schema.org/draft-07/schema#
$id:         https://json.schemastore.org/package.json
root kind:   Object
definitions: 29
properties:  61 (on root)

Exit codes: 0 on success, 1 on any failure (network / IO / parse / schema validation).

Supported schema subset

NoJsonSchema intentionally supports the schema features most common in SDK-style models:

Construct Status
type: object with properties, required, additionalProperties (bool / schema)
type: string / integer / number / boolean / null
type: array with items
Nullable via JSON Schema type: ["X", "null"] and OpenAPI nullable: true
enum (string), const
$ref (local)
$defs / definitions / OpenAPI components.schemas
allOf (inheritance pattern: $ref base + inline derived)
oneOf + discriminator (OpenAPI / Swagger polymorphism)
format: date-time / date / time / duration / uuid / uri / uri-reference / byte / binary
Integer subtypes: int8 / uint8 / byte / int16 / uint16 / int32 / uint32 / int64 / uint64
pattern, minLength / maxLength, numeric ranges ⏳ runtime validation TBD
if / then / else, not, anyOf
External $ref (cross-document)

format strings the IR doesn't recognise fall back to the base type (e.g. unknown string format → string).

Generated output

The generator emits one file per type, one formatter per type, and one public serializer class per namespace:

Generated/
  User.g.cs                          ← POCO
  Address.g.cs
  Formatters/
    UserFormatter.g.cs               ← per-type UTF-8 parser/emitter (internal static class)
    AddressFormatter.g.cs
  MyAppModelsSerializer.g.cs         ← public entry point: Cache<T> + Deserialize<T> / Serialize<T>
  _IsExternalInitShim.g.cs           ← `init` setter polyfill for netstandard
  _SetsRequiredMembersShim.g.cs      ← only when --use-required and TFM < net7

Hot-path details (commentary lives in Emit/SerializerTemplate.cs):

  • Tokenizer / Writer are ref structs with a ref byte head field on net7+ (Span<byte> + slicing on netstandard 2.1). Unsafe.Add / Unsafe.CopyBlockUnaligned instead of span-indexed access — per-byte bounds checks are elided on the hot path.
  • Vectorized scanning (net8+): whitespace skipping uses IndexOfAnyExcept and the string-terminal search (" / \ / control char) uses a cached SearchValues<byte> — both AVX2/AVX-512-accelerated in the BCL. A scalar single-byte fast-exit guards the common case so compact input — and the redundant skips after a peek — pay no call cost.
  • WriteString fast path: bulk Encoding.UTF8.GetBytes(chars, span) for ASCII-safe runs, escape only on demand.
  • Property dispatch is bucketed by UTF-8 byte length — switch (__name.Length) then SequenceEqual within bucket. Mismatches short-circuit fast.
  • Generic dispatch routes through a per-T static Cache<T> with two delegate fields. The typeof(T) == resolution runs once per CLR generic instantiation; subsequent calls are a single static-field load + one delegate invocation. The Cache + delegate types are private-nested in the Serializer, so multiple generated namespaces in the same assembly don't collide.
  • Throw* helpers are [DoesNotReturn] [MethodImpl(NoInlining)] so callers stay inlineable.

Compatibility

Target Status
.NET 8 / 9 / 10 (Native AOT, trimmed)
.NET Standard 2.0 / 2.1 (Core library)
Unity 2022.3+ / 2023.x (IL2CPP, source generator loadable by bundled Roslyn 4.3)
Generated code targets C# 11 (ref fields), so net7+ runtime

License

MIT

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

  • .NETStandard 2.1

  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

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.2.0 60 6/8/2026
1.1.0 97 5/26/2026
1.0.0 92 5/26/2026