Source Generator
Source Generator
Section titled “Source Generator”Mark a class, struct, or record with [ZodSchema] and the generator emits a static, zero-allocation validator at compile time. The [ZodSchema] attribute is generated into the ZodSharp namespace by the generator itself (assembly Purview.ZodSharp.SourceGenerators), so no extra package is needed beyond Purview.ZodSharp.
using System.ComponentModel.DataAnnotations;using ZodSharp;
[ZodSchema]public class User{ [Required] [StringLength(50, MinimumLength = 3)] public string Name { get; set; } = string.Empty;
[Range(0, 120)] public int Age { get; set; }
[EmailAddress] public string? Email { get; set; }}
var result = UserSchema.Validate(user);var validated = UserSchema.Parse(user); // throws ZodException on failureGenerated types
Section titled “Generated types”For a [ZodSchema] target type {TypeName}, the generator emits:
| Artifact | Shape |
|---|---|
{TypeName}Schema |
static partial class — the validator; access mirrors the target (public/internal/private for private nested types); contains Validate, Parse, and (when composition is enabled) ApplyAnd, ApplyOr, ApplyRefine |
{TypeName}SchemaValidator |
partial class {TypeName}SchemaValidator : IZodSchemaValidator<{TypeName}> — DI-friendly adapter with Validate / ValidateAsync; emitted only for the primary schema |
{TypeName}Validator |
sealed partial class {TypeName}Validator : IValidateOptions<{TypeName}> — emitted only when IValidateOptions support is enabled (and the target is a class) |
[assembly: ZodSchemaGenerated(typeof({TypeName}))] |
registration marker consumed by IZodSchemaFactory assembly scanning; emitted only for primary, non-nested schemas |
// Value-first composition methods (EnableComposition, default true):var adult = UserSchema.ApplyRefine(user, u => u.Age >= 18, "Must be adult");var both = UserSchema.ApplyAnd(user, u => u.Name.Length > 5, "Name too short");var either = UserSchema.ApplyOr(user, u => u.Age < 18, "Must be an adult or a minor with consent");Attribute options
Section titled “Attribute options”All options are optional.
| Property | Default | Purpose |
|---|---|---|
SchemaName |
null |
Reserved — the schema class is always named {TypeName}Schema. |
GenerateValidateMethod |
true |
Reserved — Validate is always emitted. |
GenerateParseMethod |
true |
Reserved — Parse is always emitted. |
EnableComposition |
true |
Emits ApplyAnd, ApplyOr, ApplyRefine value-first composition methods. |
CustomValidationMethodName |
null |
Name of an async custom validation method; default lookup name CustomValidationAsync. |
RefinementMethodName |
null |
Name of a synchronous refinement method; default lookup name Validate (an instance method on the model). |
GenerateIValidateOptions |
false |
Force IValidateOptions<T> generation. |
SuppressIValidateOptions |
false |
Opt out even when auto-detection would enable it. |
Custom async validation
Section titled “Custom async validation”Declare a partial {TypeName}SchemaValidator (or a static method on the model type):
public partial class UserSchemaValidator{ public async ValueTask<ValidationResult<User>> CustomValidationAsync(User value, CancellationToken ct) { await Task.Delay(1, ct); return ValidationResult<User>.Success(value); }}Requirements:
- Signature
ValueTask<ValidationResult<T>> Name(T value, CancellationToken ct). - A method declared on the model type must be
static; a method on the generated{TypeName}SchemaValidatorpartial may be an instance method. - The generated
ValidateAsyncruns the synchronousValidate, then awaits the custom method, and merges the error sets.
Synchronous refinement
Section titled “Synchronous refinement”Declare an instance method on the model (default name Validate) returning IEnumerable<ValidationError>:
[ZodSchema(RefinementMethodName = "Validate")]public class Order{ public decimal Total { get; set; }
public IEnumerable<ValidationError> Validate() { if (Total < 0) yield return ValidationError.Create("invalid_range", "Total cannot be negative", []); }}Parameterless or IEnumerable<ValidationError> Validate(RefineCtx<Order> ctx) variants are supported.
IValidateOptions support
Section titled “IValidateOptions support”Generated options validators are enabled by:
GenerateIValidateOptions = trueon the attribute, or- auto-detection:
GenerateIValidateOptionsunset, target is not a value type, and the type name ends with a configured suffix (defaultOptionsorSettings), or - MSBuild override.
MSBuild switches:
| Property | Default | Behaviour |
|---|---|---|
DisableZodSharpSourceGenerator |
unset | disables the generator entirely when truthy |
ZodSharpAutoGenerateOptionsValidators |
true |
auto-detect IValidateOptions (only explicit false disables) |
ZodSharpAutoGenerateOptionsValidatorSuffixes |
Options;Settings |
semicolon/comma-separated suffix list |
What is validated
Section titled “What is validated”- Properties must be public, non-static, non-indexer.
- A property is included when it carries any DataAnnotations attribute or its type is a source-defined complex type with a nested schema.
- Classes, structs, and records are supported; structs do not receive
IValidateOptions(ZODSGEN028 if requested). - Nested complex types are discovered recursively and get their own generated
{TypeName}Schema, even when the nested type does not itself carry[ZodSchema]. - Nullable properties are null-guarded before value-set/type validation; a nullable target rejects
nullwithinvalid_type.
See Source Generator DataAnnotations for the attribute coverage and structured issue shape, and Source Generator Diagnostics for the ZODSGEN* diagnostics.