Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ The fixture generator script is intentionally run via Bun rather than `npx tsx`
- **Packages are published under the `Purview.*` IDs.** The core `Purview.ZodSharp` package ships the validator and the source generator, with optional `Purview.ZodSharp.SystemTextJson`, `Purview.ZodSharp.NewtonsoftJson`, and `Purview.ZodSharp.AspNetCore` integration packages.
- **Targets `net8.0`, `net9.0`, and `net10.0`.** The source generator remains on `netstandard2.0` so it can run in any compiler host.
- **System.Text.Json integration.** JSON deserialize-and-validate is available for both major JSON libraries, including validating `JsonConverter<T>` instances.
- **JSON Schema interoperability.** Schemas can be exported via `Z.ToJsonSchema` and imported via `Z.FromJsonSchema`, enabling cross-language reuse with TypeScript/Zod.
- **JSON Schema interoperability.** Schemas can be exported via `Z.ToJsonSchema` and imported via `Z.FromJsonSchema`, enabling cross-language reuse with TypeScript/Zod. The import API lives in the JSON integration package's namespace (`ZodSharp.JsonSchema.SystemTextJson` or `ZodSharp.JsonSchema.NewtonsoftJson`); export stays in the core package.
- **ASP.NET Core ProblemDetails integration.** Failed validation results convert directly to `HttpValidationProblemDetails` via `result.ToHttpValidationProblemDetails()`.
- **Expanded DataAnnotations support.** `[Length]`, `[MinLength]`, `[MaxLength]`, `[RegularExpression]`, `[AllowedValues]`, `[DeniedValues]`, `[EmailAddress]`, and more, with structured size failures (`Code`, `Origin`, `Minimum`/`Maximum`, `Inclusive`, `Path`).
- **Value-first composition methods.** `.ApplyAnd()`, `.ApplyOr()`, and `.ApplyRefine()` are the supported composition surface.
Expand Down Expand Up @@ -430,19 +430,22 @@ var jsonSchema = Z.ToJsonSchema<Dictionary<string, object?>>(userSchema, new ToJ

// Serialize with your preferred JSON library
// System.Text.Json (add Purview.ZodSharp.SystemTextJson):
using ZodSharp.JsonSchema;
using ZodSharp.JsonSchema.SystemTextJson;
var systemTextJson = System.Text.Json.JsonSerializer.Serialize(jsonSchema, JsonSchemaSerializerOptions.Default);

// Newtonsoft.Json (add Purview.ZodSharp.NewtonsoftJson):
using ZodSharp.JsonSchema;
using ZodSharp.JsonSchema.NewtonsoftJson;
var newtonsoftJson = JsonConvert.SerializeObject(jsonSchema, JsonSchemaSerializerOptions.Default);
```

#### Import from JSON Schema (JSON Schema -> Purview.ZodSharp)

Add either `Purview.ZodSharp.SystemTextJson` or `Purview.ZodSharp.NewtonsoftJson` to your project, then:
Add either `Purview.ZodSharp.SystemTextJson` or `Purview.ZodSharp.NewtonsoftJson` to your project, then import that package's JSON Schema namespace:

```csharp
using ZodSharp;
using ZodSharp.JsonSchema.SystemTextJson; // or ZodSharp.JsonSchema.NewtonsoftJson

var jsonSchemaString = @"{
""type"": ""object"",
""properties"": {
Expand Down
19 changes: 15 additions & 4 deletions docs/wiki/AspNetCore-Integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,21 @@ The generated `Create` builds a typed `ErrorTypeParameters` instance (validated
parameter types and exposed through `ValidationError.Parameters`) and sets the message from
`ErrorType.FormatMessage`, so `error.Message` already reads
`Aggregate 'agg-123' (of type Invoice) failed to save` and mapping through the registry produces the
`409 Conflict` response described below. The analyzers `ZODSASP001`/`ZODSASP002`/`ZODSASP003`
(shipped with the core package) warn when a `MessageFormat` placeholder is not declared in
`Parameters`, when an `[ErrorType]` field's containing class is not `partial`, or when the field is not
`static readonly`.
`409 Conflict` response described below.

### ErrorType diagnostics

The `ErrorType` factory, its source generator, and its analyzers ship with the core `Purview.ZodSharp` package:

| ID | Severity | Meaning |
|---|---|---|
| `ZODSASP001` | Warning | A `MessageFormat` placeholder is not declared in `ErrorType.Parameters` |
| `ZODSASP002` | Warning | The containing type of an `[ErrorType]` field is not declared `partial` |
| `ZODSASP003` | Warning | An `[ErrorType]` field is not declared `static readonly` |
| `ZODSASP100` | Error | Unhandled exception in the `ErrorType` source generator |
| `ZODSASP101` | Error | The `Parameters` of an `[ErrorType]` field could not be extracted |

`ZODSASP001`–`ZODSASP003` explain why `Create`/`Throw` helpers were not generated; `ZODSASP100`/`ZODSASP101` are fatal generator failures that name the field they failed on.

Produces a `409 Conflict` `HttpValidationProblemDetails` with:

Expand Down
2 changes: 1 addition & 1 deletion docs/wiki/Cross-Platform-Interop.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ If the C# output directory is empty, the vitest suite emits a note instructing y
The same interop goal is available without fixtures via JSON Schema:

- Export: `Z.ToJsonSchema` (core package) → JSON Schema, or `z.toJSONSchema` on the TypeScript side (Zod v4+).
- Import: `Z.FromJsonSchema` (in the System.Text.Json or Newtonsoft.Json package).
- Import: `Z.FromJsonSchema` (in the System.Text.Json or Newtonsoft.Json package — namespace `ZodSharp.JsonSchema.SystemTextJson` / `ZodSharp.JsonSchema.NewtonsoftJson`).

See [JSON Schema Export](JsonSchema-Export.md) and [JSON Schema Import](JsonSchema-Import.md).

Expand Down
3 changes: 2 additions & 1 deletion docs/wiki/Getting-Started.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ dotnet add package Purview.ZodSharp.AspNetCore
- `Purview.ZodSharp.AspNetCore` — failed validation results converted to standard `ProblemDetails` / `HttpValidationProblemDetails` payloads.

> [!TIP]
> JSON Schema import (`Z.FromJsonSchema`) is provided by whichever JSON integration package you reference, so pick one. Export (`Z.ToJsonSchema`) lives in the core package.
> JSON Schema import (`Z.FromJsonSchema`) is provided by whichever JSON integration package you reference, so pick one, and import its JSON Schema namespace (`ZodSharp.JsonSchema.SystemTextJson` or `ZodSharp.JsonSchema.NewtonsoftJson`). Export (`Z.ToJsonSchema`) lives in the core package.

## First schema

Expand Down Expand Up @@ -116,6 +116,7 @@ var result = userSchema.DeserializeAndValidate(json);
var jsonSchema = Z.ToJsonSchema(userSchema, new ToJsonSchemaOptions { Title = "User" });

// JSON Schema import (requires an integration package)
// using ZodSharp.JsonSchema.SystemTextJson; // or ZodSharp.JsonSchema.NewtonsoftJson
var imported = Z.FromJsonSchema(jsonSchemaString);
```

Expand Down
4 changes: 2 additions & 2 deletions docs/wiki/Guarantees-and-Limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ Rules evaluated by the base `Validate` pipeline produce `validation_failed` erro

### JSON Schema import scope

`Z.FromJsonSchema` supports **local** `$ref` (`#/...`) references only; external `$ref` targets throw `NotSupportedException`. `FromJsonSchemaOptions` is currently empty (reserved for future options).
`Z.FromJsonSchema` supports **local** `$ref` (`#/...`) references only; external `$ref` targets throw a `NotSupportedException` that names the unsupported reference. The integration packages' `JsonSchemaSerializerOptions` read and write the JSON Schema keyword names (`$schema`, `$id`, `$ref`, `$defs`), so exported definitions round-trip; the core `JsonSchemaDefinition` type itself stays free of serializer annotations. Import types live in package-specific namespaces (`ZodSharp.JsonSchema.SystemTextJson` / `ZodSharp.JsonSchema.NewtonsoftJson`).

### Referencing both JSON integration packages

`Purview.ZodSharp.SystemTextJson` and `Purview.ZodSharp.NewtonsoftJson` both declare types with identical full names (`ZodSharp.ZExtensions`, `ZodSharp.JsonSchema.FromJsonSchemaOptions`, `FromJsonSchemaParser`, `JsonSchemaSerializerOptions`). Reference one JSON integration package; referencing both requires `extern alias`.
`Purview.ZodSharp.SystemTextJson` and `Purview.ZodSharp.NewtonsoftJson` are **mutually exclusive** integrations — pick the one that matches your JSON library. Both packages can be referenced from the same project **without `extern alias`**, because every JSON Schema import type is declared in a package-specific namespace (`ZodSharp.JsonSchema.SystemTextJson`, `ZodSharp.JsonSchema.NewtonsoftJson`) rather than an identical full name. The deserialize/serialize extension methods remain in the `ZodSharp` namespace in both packages, so import exactly one package namespace per file: importing both makes calls such as `schema.DeserializeAndValidate(json)` or `Z.FromJsonSchema(...)` ambiguous at the call site.

## Custom rules

Expand Down
6 changes: 3 additions & 3 deletions docs/wiki/JsonSchema-Export.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ Pick the JSON serializer that matches the integration package you referenced:

```csharp
// System.Text.Json (Purview.ZodSharp.SystemTextJson)
using ZodSharp.JsonSchema;
using ZodSharp.JsonSchema.SystemTextJson;
var json = System.Text.Json.JsonSerializer.Serialize(jsonSchema, JsonSchemaSerializerOptions.Default);

// Newtonsoft.Json (Purview.ZodSharp.NewtonsoftJson)
using ZodSharp.JsonSchema;
using ZodSharp.JsonSchema.NewtonsoftJson;
var json = JsonConvert.SerializeObject(jsonSchema, JsonSchemaSerializerOptions.Default);
```

`JsonSchemaSerializerOptions.Default` (camelCase, ignore nulls, indented) and `.Reading` (camelCase, ignore nulls) are provided by each integration package.
`JsonSchemaSerializerOptions.Default` (camelCase, ignore nulls, indented) and `.Reading` (camelCase, ignore nulls) are provided by each integration package and map the JSON Schema keyword names (`$schema`, `$id`, `$ref`, `$defs`), with camelCase for every other keyword. `JsonSchemaDefinition` itself carries no serializer annotations, so use these options when serializing it — that also keeps exported definitions round-tripping through the matching import API.

## Round-trip

Expand Down
21 changes: 13 additions & 8 deletions docs/wiki/JsonSchema-Import.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# JSON Schema Import

Import a JSON Schema into a Purview.ZodSharp schema with `Z.FromJsonSchema`. This API is provided by the JSON integration packages — reference either `Purview.ZodSharp.SystemTextJson` or `Purview.ZodSharp.NewtonsoftJson` (both expose the same surface).
Import a JSON Schema into a Purview.ZodSharp schema with `Z.FromJsonSchema`. This API is provided by the JSON integration packages — reference either `Purview.ZodSharp.SystemTextJson` or `Purview.ZodSharp.NewtonsoftJson`. The two JSON integrations are mutually exclusive; pick one and import its JSON Schema namespace (`ZodSharp.JsonSchema.SystemTextJson` or `ZodSharp.JsonSchema.NewtonsoftJson`).

```csharp
using ZodSharp;
using ZodSharp.JsonSchema.SystemTextJson; // or ZodSharp.JsonSchema.NewtonsoftJson

var jsonSchemaString = """
{
Expand All @@ -24,17 +25,15 @@ var result = userSchema.Validate(userData);

| Signature | Notes |
|---|---|
| `IZodSchema<object, object> FromJsonSchema(string jsonSchema, FromJsonSchemaOptions? options = null)` | parses the JSON string into a `JsonSchemaDefinition`, then into a schema |
| `IZodSchema<object, object> FromJsonSchema(JsonSchemaDefinition schema, FromJsonSchemaOptions? options = null)` | import from an already-deserialized definition |

`FromJsonSchemaOptions` is currently an empty placeholder reserved for future options.
| `IZodSchema<object, object> FromJsonSchema(string jsonSchema)` | parses the JSON string into a `JsonSchemaDefinition`, then into a schema |
| `IZodSchema<object, object> FromJsonSchema(JsonSchemaDefinition schema)` | import from an already-deserialized definition |

> [!NOTE]
> `Z.FromJsonSchema` is implemented as a C# 14 extension member on `Z`, so it only exists when a JSON integration package is referenced. `Z.ToJsonSchema` is a real static member on `Z` in the core package.

## Supported keywords

`FromJsonSchemaParser` (namespace `ZodSharp.JsonSchema`) maps:
`FromJsonSchemaParser` (namespace `ZodSharp.JsonSchema.SystemTextJson` or `ZodSharp.JsonSchema.NewtonsoftJson`) maps:

- `type` — `string` / `number` / `integer` / `boolean` / `null` / `object` / `array`.
- `enum` → `ZodUnion` of literals (a single member becomes a literal); `const` → literal.
Expand All @@ -46,8 +45,14 @@ var result = userSchema.Validate(userData);

## Limitations

- `$ref` is supported only for **local** references (`#/...`); external `$ref` targets throw `NotSupportedException`.
- The options type is currently empty; behaviour is fixed by the supported keyword set above.
- `$ref` is supported only for **local** references (`#/...`, for example `#/$defs/Name`). External `$ref` targets throw `NotSupportedException` naming the unsupported reference:

```text
External $ref 'external.json#/$defs/name' is not supported. Only local references ('#/...', for example '#/$defs/Name') can be resolved; inline or pre-resolve external schemas before importing.
```

Inline the referenced schema, or move it under the root `$defs`, before importing. Local references may be cyclic — a reference that is still being resolved becomes a lazy schema.
- The reader binds the JSON Schema keyword names `$schema`, `$id`, `$ref`, and `$defs` (plus the draft-07 `definitions`) through the integration package's `JsonSchemaSerializerOptions`, and the same options write them back, so exported definitions round-trip through either package. `JsonSchemaDefinition` itself carries no serializer annotations, so serialize it with `JsonSchemaSerializerOptions` to get keyword-compliant output.

## Cross-platform reuse

Expand Down
15 changes: 11 additions & 4 deletions docs/wiki/NewtonsoftJson-Integration.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Newtonsoft.Json Integration

The `Purview.ZodSharp.NewtonsoftJson` package adds Newtonsoft.Json deserialize-and-validate, validating converters, and JSON Schema import to the core library. All extension methods live in the `ZodSharp` namespace.
The `Purview.ZodSharp.NewtonsoftJson` package adds Newtonsoft.Json deserialize-and-validate, validating converters, and JSON Schema import to the core library. The deserialize/serialize extension methods live in the `ZodSharp` namespace; the JSON Schema import types (and `Z.FromJsonSchema`) live in the `ZodSharp.JsonSchema.NewtonsoftJson` namespace.

## Install

Expand Down Expand Up @@ -68,7 +68,14 @@ Deserialize/validation failures produce `ValidationError` entries with codes `de

## JSON Schema import

`Z.FromJsonSchema` is available with this package referenced; see [JSON Schema Import](JsonSchema-Import.md).
```csharp
using ZodSharp;
using ZodSharp.JsonSchema.NewtonsoftJson;

var schema = Z.FromJsonSchema(jsonSchemaString);
```

See [JSON Schema Import](JsonSchema-Import.md) for the supported keywords, `$ref` handling, and the `JsonSchemaSerializerOptions` defaults.

## System.Text.Json vs Newtonsoft.Json

Expand All @@ -82,5 +89,5 @@ Deserialize/validation failures produce `ValidationError` entries with codes `de
| Invalid-data exception | `System.Text.Json.JsonException` | `JsonSerializationException` |
| JSON plumbing | `JsonElement` | `JToken`/`JObject`/`JArray` |

> [!WARNING]
> Both packages declare types with identical full names (`ZodSharp.ZExtensions`, `ZodSharp.JsonSchema.FromJsonSchemaOptions`, `ZodSharp.JsonSchema.FromJsonSchemaParser`, `ZodSharp.JsonSchema.JsonSchemaSerializerOptions`). Referencing both packages in one project creates type ambiguity unless `extern alias` is used — reference one JSON integration package.
> [!NOTE]
> `Purview.ZodSharp.SystemTextJson` and `Purview.ZodSharp.NewtonsoftJson` are mutually exclusive integrations — pick the one that matches your JSON library. Both packages can be referenced from the same project without `extern alias` (their JSON Schema types live in the `ZodSharp.JsonSchema.SystemTextJson` / `ZodSharp.JsonSchema.NewtonsoftJson` namespaces), but the deserialize/serialize extension overloads share names, so import exactly one package namespace per file.
2 changes: 1 addition & 1 deletion docs/wiki/Release-Flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Purview.ZodSharp releases are driven by the shared [purview-dev/build](https://g

## Versioning

The package version comes from `package.json` (`version` field). The repo is currently on the `2.0.0-prerelease.*` line. Bump `package.json` to release a new version.
The package version comes from `package.json` (`version` field). The current stable line is `2.0.0`; bump `package.json` to release a new version (prerelease builds use a `MAJOR.MINOR.PATCH-prerelease.N` suffix).

Package identities are `Purview.ZodSharp.*` (core, SystemTextJson, NewtonsoftJson, AspNetCore). Central package management lives in `Directory.Packages.props`; package versions there are minimum requirements, not exact pins, so the resolved graph can drift.

Expand Down
4 changes: 3 additions & 1 deletion docs/wiki/Source-Generator-Diagnostics.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Source Generator Diagnostics

The `[ZodSchema]` generator ships an analyzer (category `ZodSharp.SourceGenerator`) that reports configuration and usage problems at compile time. All diagnostics below are errors, enabled by default.
The `[ZodSchema]` generator ships an analyzer (category `ZodSharp.SourceGenerator`) that reports configuration and usage problems at compile time. Every diagnostic below is enabled by default; `ZODSGEN033` is a warning and the rest are errors.

| ID | Meaning |
|---|---|
Expand Down Expand Up @@ -35,6 +35,8 @@ The `[ZodSchema]` generator ships an analyzer (category `ZodSharp.SourceGenerato
| ZODSGEN035 | The `OnZodValidate` refinement hook is not declared as `partial void OnZodValidate(RefineCtx<T> context)` (wrong modifiers, return type, or parameters) |
| ZODSGEN036 | A member still uses the retired synchronous refinement contract (`IEnumerable<ValidationError> Validate()`); implement `OnZodValidate` instead |

IDs `ZODSGEN002` and `ZODSGEN022`–`ZODSGEN026` are intentionally unused; rule identifiers are never renumbered or re-used.

## Suppressing

Diagnostics can be suppressed per-project or per-site with the standard `#pragma warning disable ZODSGEN006` / `NoWarn` mechanisms. Refer to the analyzer's shipped release notes (`AnalyzerReleases.Shipped.md` / `AnalyzerReleases.Unshipped.md` in the generator project) for the canonical catalog.
11 changes: 9 additions & 2 deletions docs/wiki/SystemTextJson-Integration.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# System.Text.Json Integration

The `Purview.ZodSharp.SystemTextJson` package adds System.Text.Json deserialize-and-validate, validating converters, and JSON Schema import to the core library. All extension methods live in the `ZodSharp` namespace.
The `Purview.ZodSharp.SystemTextJson` package adds System.Text.Json deserialize-and-validate, validating converters, and JSON Schema import to the core library. The deserialize/serialize extension methods live in the `ZodSharp` namespace; the JSON Schema import types (and `Z.FromJsonSchema`) live in the `ZodSharp.JsonSchema.SystemTextJson` namespace.

## Install

Expand Down Expand Up @@ -65,7 +65,14 @@ Deserialize/validation failures produce `ValidationError` entries with codes `de

## JSON Schema import

`Z.FromJsonSchema` is available with this package referenced; see [JSON Schema Import](JsonSchema-Import.md).
```csharp
using ZodSharp;
using ZodSharp.JsonSchema.SystemTextJson;

var schema = Z.FromJsonSchema(jsonSchemaString);
```

See [JSON Schema Import](JsonSchema-Import.md) for the supported keywords, `$ref` handling, and the `JsonSchemaSerializerOptions` defaults.

## Comparing with Newtonsoft

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zodsharp",
"version": "2.0.0-prerelease.28",
"version": "2.0.0",
"private": true,
"license": "MIT",
"author": {
Expand Down
1 change: 1 addition & 0 deletions src/ZodSharp.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
</Folder>
<Folder Name="/tests/">
<Project Path="tests/AspNetCore.UnitTests/AspNetCore.UnitTests.csproj" />
<Project Path="tests/JsonInterop.UnitTests/JsonInterop.UnitTests.csproj" />
<Project Path="tests/NewtonsoftJson.UnitTests/NewtonsoftJson.UnitTests.csproj" />
<Project Path="tests/SourceGenerators.UnitTests/SourceGenerators.UnitTests.csproj" />
<Project Path="tests/SystemTextJson.UnitTests/SystemTextJson.UnitTests.csproj" />
Expand Down
1 change: 1 addition & 0 deletions src/src/Examples.CLI/JsonSchemaExamples.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Newtonsoft.Json;
using ZodSharp.JsonSchema;
using ZodSharp.JsonSchema.NewtonsoftJson;

namespace ZodSharp.Examples.CLI;

Expand Down
Loading
Loading