OrionGuard.SchemaExport 7.0.0

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

OrionGuard.SchemaExport

Exports the validation rules already declared on a model as a JSON Schema document or a TypeScript interface, so the frontend consumes the rules instead of a hand-written copy that drifts.

dotnet add package OrionGuard.SchemaExport
using Moongazing.OrionGuard.Attributes;
using Moongazing.OrionGuard.SchemaExport;

public sealed class CreateUserRequest
{
    [NotNull, Email] public string Email { get; set; } = "";
    [Length(8, 100)] public string Password { get; set; } = "";
    [Range(13, 120)] public int Age { get; set; }
    [Positive] public decimal Budget { get; set; }
    public Uri? Website { get; set; }
}

public static class ExportContracts
{
    public static void Run()
    {
        File.WriteAllText("create-user.schema.json", JsonSchemaExporter.Export<CreateUserRequest>());
        File.WriteAllText("create-user.ts", TypeScriptExporter.Export<CreateUserRequest>());
    }
}

create-user.schema.json is an indented JSON Schema draft 2020-12 document:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "CreateUserRequest",
  "type": "object",
  "properties": {
    "email": { "type": "string", "format": "email" },
    "password": { "type": "string", "minLength": 8, "maxLength": 100 },
    "age": { "type": "integer", "minimum": 13, "maximum": 120 },
    "budget": { "type": "number", "exclusiveMinimum": 0 },
    "website": { "type": ["string", "null"], "format": "uri" }
  },
  "required": ["email"]
}

and create-user.ts is types plus documented constraints:

// Generated by OrionGuard.SchemaExport from YourApp.CreateUserRequest.
// Types only: the constraints below are documented here, not enforced here.

export interface CreateUserRequest {
  /** required; format: email */
  email: string;
  /** 8 to 100 characters */
  password?: string;
  /** between 13 and 120 */
  age?: number;
  /** greater than 0 */
  budget?: number;
  /** format: uri */
  website?: string | null;
}

The core OrionGuard package comes along as a dependency; nothing else does, because the exporters use System.Text.Json only. JsonSchemaExporter.Export<T>(Utf8JsonWriter) writes the same object into a writer you own, for streaming it or nesting it in a larger document, and TypeScriptExporter.Export<T>() uses \n line endings on every platform so the file is byte-identical wherever it was generated.

Rule mapping

Attribute JSON Schema TypeScript comment
[NotNull] adds the member to required required
[NotEmpty] required, plus minLength: 1 and pattern: \S on a string, or minItems: 1 on a collection required; not blank
[Length(min, max)] minLength, maxLength (minItems, maxItems on a collection) min to max characters
[Range(min, max)] minimum, maximum between min and max
[Regex(pattern)] pattern, when the expression is also ECMAScript regex pattern: ...
[Email] format: email format: email
[Positive] exclusiveMinimum: 0 (the numeric form JSON Schema 2020-12 uses) greater than 0
any other ValidationAttribute x-orionguard-unsupported // not enforced here: ...

[NotEmpty] rejects null as well as the empty value, so it makes a member required on its own. On a string it is IsNullOrWhiteSpace, not a length check, so minLength: 1 alone would accept " " and the client would send what the server rejects; the unanchored \S pattern — "holds a non-whitespace character" — closes that gap.

Attributes combine rather than overwrite: two [Length] rules produce the bounds that satisfy both, and two patterns on one member are ANDed under allOf, because pattern holds a single expression.

Type mapping

CLR JSON Schema TypeScript
string, char string string
integral types integer number
float, double, decimal number number
bool boolean boolean
Guid string, format: uuid string
Uri string, format: uri string
DateTime, DateTimeOffset string, format: date-time string
DateOnly / TimeOnly string, format: date / time string
TimeSpan string string
enum string plus an enum list of the member names a union of the member names
IEnumerable<T>, T[] array with items, nested to any depth T[], T[][], …
byte[] string, contentEncoding: base64 string
dictionary object Record<string, unknown>
any other class, record or struct $ref into $defs its own export interface
object unconstrained unknown

OrionGuard has no URL attribute, so a Uri member is the only way the exporters know a string holds a URL.

Details that decide what the artifact says

  • Members are the public instance properties with a getter — the same surface AttributeValidator and the Swagger schema filter see. Indexers and [JsonIgnore] members are skipped.
  • The serialized name is [JsonPropertyName] when present, otherwise the camelCase form of the property name. A name that is not a valid TypeScript identifier is written quoted and escaped, or the declaration would not parse.
  • A member is required only when an OrionGuard rule requires it. A non-nullable CLR type on its own is not enough: it removes the null branch from the type but leaves the member optional.
  • A nullable member gets "type": ["x", "null"], a nullable nested type an anyOf with a null branch, and element nullability is carried at every level: List<string?> exports (string | null)[], List<string> exports string[].
  • A property in an assembly compiled without nullable reference types is treated as nullable, because nothing there rules null out — the same for elements of a collection that reaches IEnumerable<T> without being an array or a single-argument generic.
  • Enums are exported by name, which is what JsonStringEnumConverter writes. A payload that serializes enums as numbers will not match the schema.
  • Nested types are emitted once under $defs and referenced; a type that references itself, directly or through a cycle, points back at the document root ("$ref": "#"). Two nested types with the same short name would collide, so the second is published under its namespace-qualified name.

What this does not do

  • It cannot see a rule written as code. Rules registered on an AbstractValidator<T>, or built with Validate.For / Validate.Nested / Validate.CrossProperties, are C# delegates, and reflection cannot read a delegate's body. Those rules are invisible to the exporters and no trace of them reaches the artifact — not even a note. If a rule has to reach the frontend, declare it as an attribute.

  • It reports what it cannot express, rather than dropping it. A rule the exporters cannot translate is listed under x-orionguard-unsupported on the schema that declares the member, and gets a // not enforced here: comment above the TypeScript member:

    "x-orionguard-unsupported": [
      { "member": "confirmation", "rule": "MatchesPasswordAttribute" }
    ]
    

    That is the honest half of the previous point: an attribute the exporter does not know is named in the artifact, a delegate rule is not.

  • A .NET-only regex is not exported. RegexAttribute lands in that same list when the expression uses constructs ECMAScript lacks: an inline option ((?i)), a .NET anchor (\A, \Z, \z, \G), a Unicode category (\p{...}), a named or balancing group, an atomic or conditional group, or character-class subtraction ([a-z-[aeiou]]). A JSON Schema pattern is an ECMAScript regex with no flags, so copying one across would make the artifact match what the server rejects. Lookaround, non-capturing groups and ordinary escapes are shared and are copied through. The screen is deliberately pessimistic: it may report a pattern an engine would have handled, which costs a constraint and says so, rather than shipping one that lies.

  • TypeScript output enforces nothing. It is types plus doc comments — no runtime checks, no validator. The JSON Schema is the artifact a validator can run.

  • It exports one type at a time. Export<T>() walks T and what T references; there is no assembly sweep and no file layout — you choose what to export and where to write it.

  • Not trimming- or NativeAOT-safe. Both Export<T> methods are marked [RequiresUnreferencedCode] because they walk public properties, and their property types, with reflection. Under trimming or NativeAOT, root the exported types (with DynamicDependency, for example) or the artifact will silently be missing the members that were trimmed away.

Targets

net8.0, net9.0, net10.0.

With the rest of OrionGuard

OrionGuard (the attributes it reads) · OrionGuard.Swagger (the same constraints on the OpenAPI schemas Swashbuckle generates) · OrionGuard.OpenApi (the opposite direction: a validator from an OpenAPI document)

Documentation

License

MIT. See LICENSE.txt.

Product Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
7.0.0 88 9/20/2026