Skip to content

Core Concepts

Preview ZodSharp Reviewed 2026-09-16 purview-dev/zodsharp aspnetcore c-sharp csharp dotnet json-schema newtonsoft-json schema source-generator system-text-json validation zero-allocation zod

Every schema derives from ZodType<TOutput, TInput> (namespace ZodSharp.Core). Validation is a two-phase pipeline:

  1. ParseInternal — each schema overrides this hook to perform its type check and traversal (rejecting null where not allowed, coercing types, walking objects/arrays/tuples/unions, producing structured failures).
  2. Rules — on success, the accumulated IValidationRule<TOutput> structs are evaluated. A failing rule emits a ValidationError with code "validation_failed".
ValidationResult<TOutput> result = schema.Validate(value);
Member Behaviour
Validate(TInput value) Returns a ValidationResult<TOutput>. Never throws.
SafeParse(TInput value) Alias of Validate.
Parse(TInput value) Returns the validated TOutput, or throws ZodException on failure (via GetValueOrThrow()).
ValidateAsync(TInput value, CancellationToken) ValueTask wrapper around Validate. The pipeline is synchronous; this exists for interface symmetry and the source generator’s custom async validation.

ValidationResult<T> is a readonly record struct in ZodSharp.Core:

  • bool IsSuccess — annotated [MemberNotNullWhen(true, nameof(Value))].
  • T? Value — the validated value; valid only when IsSuccess.
  • ImmutableArray<ValidationError> Errors — populated on failure.

Static factories: Success(value), Failure(ValidationError), Failure(ImmutableArray<ValidationError>), Failure(IEnumerable<ValidationError>), and Merge(lhs, rhs) (succeeds only if both succeed; concatenates errors).

ValidationError is a readonly record struct carrying machine-readable metadata:

  • Code — e.g. "invalid_type", "too_small", "too_big", "invalid_union", "missing_field", "unrecognized_key", "validation_failed", "refinement_failed", "transform_error".
  • Message — human-readable message.
  • PathImmutableArray<string>, e.g. ["user", "email"]; array indexes appear as string segments such as "0".
  • Origin — category for structured size issues ("string", "array", "collection").
  • Minimum / Maximum — inclusive bounds for size issues.
  • Inclusive — whether the bound is inclusive.
  • ParametersIReadOnlyDictionary<string, object?>, e.g. union failures carry every option error under "errors".

Create custom issues with ValidationError.Create(code, message, path, parameters, origin, minimum, maximum, inclusive).

ZodException (namespace ZodSharp.Core) is thrown by Parse and GetValueOrThrow(). It exposes ImmutableArray<ValidationError> Errors. Its ToString() renders one line per error: "{joinedPath}: {Message} ({Code})".

try
{
var value = schema.Parse("AB");
}
catch (ZodException ex)
{
foreach (var error in ex.Errors)
Console.WriteLine($"{string.Join(".", error.Path)}: {error.Message}");
}
  • Schemas are classes deriving from ZodType<TOutput, TInput>; the fluent methods return this (or a wrapping schema) so chains read naturally.
  • Rules are readonly record struct implementations of IValidationRule<T> (bool IsValid(in T value), string GetErrorMessage(in T value)) — zero allocation.
  • ValidateSpan(ReadOnlySpan<char> value) is available on ZodString for span-based validation.
  • A schema’s Description is set with .Describe("...").

ZodType composes via wrappers rather than mutation. The base type guards that input and output types match, then returns a new schema:

  • Transform<TNew>(Func<TOutput, TNewOutput>)ZodTransform<TOutput, TNewOutput>.
  • Refine(Func<TOutput, bool>, string? message)ZodRefinement<TOutput>.
  • SuperRefine(Action<RefineCtx<TOutput>>)ZodSuperRefinement<TOutput>.
  • Pipe<TTarget>(IZodSchema<TTarget, TOutput>)ZodPipe<TOutput, TTarget>.
  • Catch(TOutput | Func<TOutput, ImmutableArray<ValidationError>, TOutput>)ZodCatch<TOutput>.
  • Prefault(TOutput)ZodPrefault<TOutput>.
  • Default(TOutput)ZodDefault<TOutput>.
  • And(IZodSchema<TOutput, TOutput>)ZodIntersection<TOutput>.
  • Or<TOther>(IZodSchema<TOther, TOther>)ZodTypedUnion<TOutput, TOther>.

See Composition and Transforms for details and semantics.

Schemas report optionality through the internal IOptionalSchema interface:

  • IsOptionaltrue for ZodOptional, ZodNullable, ZodDefault, ZodPrefault.
  • ProvidesValueOnMissingtrue for ZodDefault and ZodPrefault; object fields route missing values through Validate(null!) to inject the produced value.
  • IAcceptsNull.ValidateNull() is implemented by nullable/optional/default/prefault schemas and ZodNull; object-field and union wrappers route null input to it instead of failing coercion.

Object fields and union options wrap typed schemas so boxed values from a Dictionary<string, object?> can be validated:

  • null routes to IAcceptsNull.ValidateNull() where supported.
  • Exact-typed values reuse the original boxed value.
  • Numeric coercion uses IConvertible (invariant culture), so a boxed long can satisfy a Z.Number() field.
  • Non-nullable value types reject null; anything else falls through to failure.

IZodSchemaFactory is the registry that resolves validators by type. See Dependency Injection.