Validation Rules Reference
Validation Rules Reference
Section titled “Validation Rules Reference”Every built-in validation rule lives in the ZodSharp.Rules namespace and is a readonly record struct implementing ZodSharp.Core.IValidationRule<T>. Rules are the smallest unit of validation: a schema is a type/structural check followed by an ordered list of rules, and each failing rule adds a ValidationError to the result.
This page is the catalogue of the rules that ship with the core package. For authoring your own, see Custom Rules.
The rule contract
Section titled “The rule contract”namespace ZodSharp.Core;
public interface IValidationRule<T>{ bool IsValid(in T value); string GetErrorMessage(in T value); string Code => "validation_failed";}- String rules additionally implement
ZodSharp.Core.IStringValidationRule(bool IsValid(ReadOnlySpan<char>)/string GetErrorMessage(ReadOnlySpan<char>)) soZodString.ValidateSpan/IsValidSpancan validate without materialising the input. - Every rule exposes its error identity as public constants:
public const string ErrorCodeandpublic const string MessageFormat(a{0}-stylestring.Formattemplate).Codereturns the rule’s effective code — a per-usagecodeoverride when supplied, otherwiseErrorCode— andGetErrorMessageformatsMessageFormatwithRuleMessage.Format(which caches the parsedCompositeFormat). - A constructor
messageparameter, when supplied, overrides the formattedMessageFormat.MessageFormatis the fallback, not the only message. - The analyzer reports
ZODSGEN042when a source-declared rule omitsErrorCode/MessageFormat. See Source Generator Diagnostics.
Rules can be used three ways:
using ZodSharp;using ZodSharp.Rules;
// 1. Standalonevar rule = new EmailRule();if (!rule.IsValid("not-an-email")) Console.WriteLine(rule.GetErrorMessage("not-an-email"));
// 2. Attached to a schema (AddRule or the generic Rule helper)var schema = Z.String().AddRule(new EmailRule());var same = Z.String().Rule(new EmailRule());
// 3. Surfaced as a DataAnnotations attribute the [ZodSchema] generator honours// (built-in mappings, or a custom rule via [ZodRule]). See Source Generator DataAnnotations.Tests can assert against the rule’s constants instead of duplicating literals:
var result = Z.String().Email().Validate("not-an-email");await Assert.That(result.Errors[0].Code).IsEqualTo(EmailRule.ErrorCode); // "invalid_string"await Assert.That(result.Errors[0].Message).IsEqualTo( string.Format(CultureInfo.CurrentCulture, EmailRule.MessageFormat, "not-an-email"));Error codes
Section titled “Error codes”The built-in rules report one of the following Zod-compatible codes:
| Code | Meaning | Emitted by |
|---|---|---|
invalid_string | A string did not match the required format | all string format rules |
invalid_type | A value was not the expected type/shape (e.g. not a whole number) | IntRule<T> |
too_small | A length or value fell below a lower bound | MinLengthRule, MinValueRule<T>, GreaterThanRule<T> |
too_big | A length or value exceeded an upper bound | MaxLengthRule, MaxValueRule<T>, LessThanRule<T>, SafeIntegerRule |
not_multiple_of | A number was not a multiple of the divisor | MultipleOfRule<T> |
not_finite | A number was NaN or infinite | FiniteRule<T> |
invalid_value | A value was a rejected sentinel or was not in the allowed set | NonSentinelRule<T>, AllowedValuesRule<T>, DeniedValuesRule<T> |
invalid_enum_value | A value was not a defined member of the enum type, or was an excluded member | EnumRule<TEnum> |
missing_field | A required value was absent (or empty/whitespace-only) | RequiredRule<T> |
invalid_length | A string length fell outside the required range | LengthRule, StringLengthRule |
invalid_range | A comparable value fell outside the required range | RangeRule<T> |
mismatch | A value did not equal the expected value | CompareRule |
validation_failed | Fallback for rules that do not declare an identity | any rule without ErrorCode/IZodRule |
String rules
Section titled “String rules”Fluent methods live on ZodString (ZodSharp.Schemas). The Span column marks rules that implement IStringValidationRule and therefore stay on the zero-allocation span path.
Length
Section titled “Length”| Rule | Constructor | Fluent method | Code | Message format | Span |
|---|---|---|---|---|---|
MinLengthRule | (int minLength) | Min(n), Length(n) | too_small | String must be at least {0} characters long, but got {1} | yes |
MaxLengthRule | (int maxLength) | Max(n), Length(n) | too_big | String must be at most {0} characters long, but got {1} | yes |
Z.String().Length(n) adds both rules, so the string must be exactly n characters.
Format
Section titled “Format”| Rule | Constructor | Fluent method | Code | Message format | Span |
|---|---|---|---|---|---|
EmailRule | () | Email() | invalid_string | Invalid email format: {0} | yes |
RegexRule | (Regex pattern, string? message) or (string pattern, string? message) | Regex(...) | invalid_string | String does not match the required pattern: {0} | yes |
UrlRule | (string? message) | Url() | invalid_string | Invalid URL format: {0} | no |
UriRule | (UriKind uriKind, string? message) | Uri(), Uri(uriKind) | invalid_string | Invalid Uri, kind: {0}, format: {1} | no |
PhoneRule | (string? message) | Phone() | invalid_string | Invalid phone number format: {0} | yes |
E164Rule | (string? message) | E164() | invalid_string | Invalid E.164 phone number: {0} | yes |
CreditCardRule | (string? message) | CreditCard() | invalid_string | Invalid credit card number format: {0} | yes |
Base64StringRule | (string? message) | Base64String() | invalid_string | Invalid Base64 string format: {0} | no |
Base64UrlRule | (string? message) | Base64Url() | invalid_string | Invalid base64url string: {0} | yes |
UUIDRule | (string? message) or (UuidVersion version, string? message) | UUID() / UUID(version) | invalid_string | Invalid UUID format: {0} (or Invalid UUID v{0} format: {1}) | yes |
ULIDRule | (string? message) | ULID() | invalid_string | Invalid ULID: {0} | yes |
JWTRule | (string? message) | JWT() | invalid_string | Invalid JWT: {0} | yes |
IPAddressRule | (string? message, string? code) or (IPAddressRuleType ruleType, string? message, string? code) | IP() / IP(ruleType) | invalid_string | Invalid {1} address: {0} | yes |
HexRule | (string? message) | Hex() | invalid_string | Invalid hexadecimal string: {0} | yes |
DateStringRule | (string? message) | Date() | invalid_string | Invalid date: {0} | yes |
TimeStringRule | (string? message) | Time() | invalid_string | Invalid time: {0} | yes |
DatetimeStringRule | (string? message) | Datetime() | invalid_string | Invalid date-time: {0} | yes |
NanoidRule | (string? message) | Nanoid() | invalid_string | Invalid nanoid: {0} | yes |
Cuid2Rule | (string? message) | Cuid2() | invalid_string | Invalid CUID2: {0} | yes |
StartsWithRule | (string prefix, StringComparison comparison = StringComparison.Ordinal, string? message, string? code) | StartsWith(prefix, comparison) | invalid_string | String must start with '{0}', but got '{1}' | yes |
EndsWithRule | (string suffix, StringComparison comparison = StringComparison.Ordinal, string? message, string? code) | EndsWith(suffix, comparison) | invalid_string | String must end with '{0}', but got '{1}' | yes |
IncludesRule | (string substring, string? message, string? code) | Includes(substring) | invalid_string | String must contain '{0}', but got '{1}' | yes |
EmojiRule | (string? message) | Emoji() | invalid_string | Invalid emoji: {0} | yes |
XidRule | (string? message) | Xid() | invalid_string | Invalid XID: {0} | yes |
KsuidRule | (string? message) | Ksuid() | invalid_string | Invalid KSUID: {0} | yes |
DurationRule | (string? message) | Duration() | invalid_string | Invalid duration: {0} | yes |
GuidRule | (string? message) | Guid() | invalid_string | Invalid GUID: {0} | yes |
CidrRule | (CidrRuleType ruleType, string? message, string? code) | Cidr() / Cidr(ruleType) | invalid_string | Invalid {1} CIDR: {0} | yes |
Every rule constructor also accepts an optional string? code = null after message; when set it overrides the reported error code (otherwise the rule’s own ErrorCode is reported).
Behaviour notes:
EmailRulematches a compiled, case-insensitive regex (^[^@\s]+@[^@\s]+\.[^@\s]+$).RegexRuleuses the suppliedRegex; theZ.String().Regex(string)overload compiles the pattern with a 100 ms timeout. Its message’s{0}is the pattern, not the value.UrlRuleaccepts the compiled HTTP(S) regex or an absolutehttp/httpsURI (Uri.TryCreate), so it may allocate when the fast regex misses.UriRulevalidates withUri.TryCreateagainst the suppliedUriKindand therefore allocates.Z.String().Uri()defaults toUriKind.RelativeOrAbsolute; pass an explicitUriKindto require an absolute or relative URI.PhoneRulemirrors[Phone]: digits plus() .+-, with at least one digit.E164Rulerequires+, a first digit1-9, and 7-15 digits in total.CreditCardRulemirrors[CreditCard]: Luhn check, ignoring spaces and hyphens.Base64StringRulemirrors[Base64String]:Convert.FromBase64Stringmust succeed.Base64UrlRuleaccepts URL-safe base64 (A-Z a-z 0-9 - _) with no padding; a length of4n+1is rejected.UUIDRuleaccepts RFC 9562 versions 1-8 with a variant nibble of8-9/a-b, plus the nil and max UUIDs. The versioned constructor requires a specific version and rejects nil/max.ULIDRulerequires 26 Crockford base32 characters, with the first character0-7.JWTRulerequires three non-empty base64url-encoded segments separated by periods.IPAddressRuleaccepts IPv4 or IPv6 (IPAddress.TryParse); pass anIPAddressRuleTypeto requireIPv4,IPv6, orAny(the default).HexRuleaccepts any run of ASCII hex digits; the empty string is valid, matching Zod.DateStringRuleparsesyyyy-MM-dd(invariant culture);TimeStringRuleacceptsHH:mm, optionally:ssand fractional seconds;DatetimeStringRulerequiresyyyy-MM-dd+T+HH:mm:ss[.fff]+Z.NanoidRulerequires exactly 21 URL-safe characters;Cuid2Rulerequires non-empty lowercase alphanumerics.StartsWithRuleandEndsWithRuleuse the suppliedStringComparison(defaultOrdinal);IncludesRuleuses ordinal comparison.EmojiRulemirrors Zod’semoji(): every rune must be anExtended_PictographicorEmoji_Componentcode point, and at least one must be an anchor (a pictograph, a regional indicator, or a keycap). The Unicode tables are generated from Unicode 16.0emoji-data.txt, because .NET’s regex engine does not expose theExtended_Pictographicproperty.XidRulerequires exactly 20 base32hex characters (0-9,a-v/A-V);KsuidRulerequires exactly 27 base62 characters.DurationRuleaccepts ISO 8601-1 durations; the 8601-2 extensions (negative durations and fractional/negative components) are rejected, matching Zod.GuidRuleaccepts any 8-4-4-4-12 hexadecimal identifier, including nil and max;UUIDRuleadditionally checks the RFC 9562 version and variant nibbles.CidrRuleaccepts an IPv4 or IPv6 block; pass aCidrRuleTypeto requireIPv4orIPv6(Anyis the default).
Number rules
Section titled “Number rules”Fluent methods live on ZodNumber (ZodSharp.Schemas), which validates double.
| Rule | Constructor | Fluent method | Code | Message format |
|---|---|---|---|---|
MinValueRule<T> | (T minValue) | Min(v), NonNegative() | too_small | Value must be at least {0}, but got {1} |
MaxValueRule<T> | (T maxValue) | Max(v), NonPositive() | too_big | Value must be at most {0}, but got {1} |
GreaterThanRule<T> | (T exclusiveMinimum) | Gt(v), Positive() | too_small | Value must be greater than {0}, but got {1} |
LessThanRule<T> | (T exclusiveMaximum) | Lt(v), Negative() | too_big | Value must be less than {0}, but got {1} |
GreaterThanOrEqualRule<T> | (T minValue, string? message) | Gte(v) | too_small | Value must be greater than or equal to {0}, but got {1} |
LessThanOrEqualRule<T> | (T maxValue, string? message) | Lte(v) | too_big | Value must be less than or equal to {0}, but got {1} |
EvenRule<T> | (string? message) | Even() | invalid_value | Number must be even, but got {0} |
OddRule<T> | (string? message) | Odd() | invalid_value | Number must be odd, but got {0} |
MultipleOfRule<T> | (T divisor, string? message) | MultipleOf(divisor) | not_multiple_of | Number must be a multiple of {0}, but got {1} |
FiniteRule<T> | (string? message) | Finite() | not_finite | Number must be finite, but got {0} |
SafeIntegerRule | (string? message) | Safe() | too_big | Number must be a safe integer, but got {0} |
IntRule<T> | () | Int() | invalid_type | Expected integer, but got {0} |
Behaviour notes:
- All six bound rules (
MinValueRule<T>,MaxValueRule<T>,GreaterThanRule<T>,LessThanRule<T>,GreaterThanOrEqualRule<T>,LessThanOrEqualRule<T>) are generic overT : IComparable<T>, so they can be reused with any comparable type (the fluent methods close them withdouble).ZodBigIntandZodDateclose the generic rules withlongandDateTime. - The arithmetic rules (
IntRule<T>,FiniteRule<T>,MultipleOfRule<T>,EvenRule<T>,OddRule<T>) are generic overT : INumber<T>, so they close withint,long,double,decimal, and every other numeric type; the fluent methods close them withdouble. Positive()isGreaterThanRule<double>(0.0),Negative()isLessThanRule<double>(0.0),NonNegative()isMinValueRule<double>(0.0), andNonPositive()isMaxValueRule<double>(0.0).EvenRule<T>andOddRule<T>treat a value as even whenvalue % 2 == 0.MultipleOfRule<T>throwsArgumentExceptionwhen the divisor is0, and compares the distance to the nearest multiple against a relative tolerance of1e-12(so0.3is accepted for a divisor of0.1). Integer types cannot represent the tolerance, so their comparison is exact.FiniteRule<T>rejectsNaNand infinities viaT.IsFinite.SafeIntegerRuleis intentionallydouble-only (“safe integer” is a JavaScriptNumberconcept); it requires a whole number withinint.MinValue..int.MaxValue.IntRule<T>requiresvalue % T.One == T.Zero, which also rejects infinities (a truncate comparison would accept them).
Value rules
Section titled “Value rules”| Rule | Constructor | Fluent method | Code | Message format |
|---|---|---|---|---|
NonSentinelRule<T> | (string? message) | NonSentinel() | invalid_value | Value is a sentinel value, but got {0} |
NullOrNonWhiteSpaceRule | (string? message, string? code) | — | invalid_string | Value must be null or non-whitespace, but got '{0}' |
NonSentinelRule<T> rejects the framework default/boundary values an ORM commonly stores to mean “no value”: Guid.Empty; DateTime, DateTimeOffset, DateOnly, and TimeOnly MinValue/MaxValue; and null/empty/whitespace strings. Types without a known sentinel always pass, so the rule never rejects a type it does not understand. It detects sentinels with a typeof(T) dispatch and reinterprets the value in place, so no boxing occurs.
NullOrNonWhiteSpaceRule is the null-tolerant counterpart: it accepts null but rejects an empty or whitespace-only string. It exists for nullable scalar value objects, which round-trip JSON null, so their schema must accept null while still rejecting whitespace — [RequiredZod] always rejects null and so cannot express it. Apply it to a nullable scalar type with [NullOrNonWhiteSpace]; a rule written against the underlying string? value is adapted to the value object automatically.
The fluent NonSentinel() method is declared on ZodType<TOutput, TInput>, so it is available on every schema and closes the rule with the schema’s output type. ZodString and ZodDate override it with a covariant return type so the fluent chain keeps the concrete schema:
var schema = Z.Date().NonSentinel();var result = schema.Validate(DateTime.MinValue);// result.Errors[0].Code == NonSentinelRule<DateTime>.ErrorCode ("invalid_value")
// Covariant overrides keep the concrete schema, so later fluent calls still compile.var chained = Z.String().NonSentinel().Min(3);The same rule can be attached directly with AddRule/Rule, or through the shipped [NonSentinel] attribute, which closes the open generic with the annotated member/scalar type — see Custom Rules.
DataAnnotations-mirroring rules
Section titled “DataAnnotations-mirroring rules”These rules mirror System.ComponentModel.DataAnnotations attributes as first-class rules, so the same behaviour is available standalone, through the fluent API, or through a generated attribute:
| Rule | Constructor | Fluent method | Code | Message format |
|---|---|---|---|---|
RequiredRule<T> | (bool allowEmptyStrings = false, bool trimWhitespace = false, string? message, string? code) | ZodString.Required(...) | missing_field | Field is required |
RangeRule<T> | (T minimum, T maximum, bool minimumIsExclusive = false, bool maximumIsExclusive = false, string? message, string? code) | ZodNumber.Range(...), ZodBigInt.Range(...), ZodDate.Range(...) | invalid_range | Value must be between {0} and {1}, but got {2} |
LengthRule | (int minimum, int maximum, string? message, string? code) | ZodString.Length(min, max) | invalid_length | String length must be between {0} and {1} characters, but got {2} |
StringLengthRule | (int maximumLength, int minimumLength = 0, string? message, string? code) | ZodString.StringLength(max, min) | invalid_length | String must be at most {0} characters long and at least {1}, but got {2} |
CompareRule | (string other, StringComparison comparison = Ordinal, string? message, string? code) | ZodString.Compare(...) | mismatch | Value must match '{0}', but got '{1}' |
AllowedValuesRule<T> | (T[] values, string? message, string? code) | AllowedValues(values) | invalid_value | Value '{0}' is not one of the allowed values |
DeniedValuesRule<T> | (T[] values, string? message, string? code) | DeniedValues(values) | invalid_value | Value '{0}' is one of the denied values |
RequiredRule<T> always rejects null. When allowEmptyStrings is false an empty string is rejected as well; trimWhitespace additionally treats a whitespace-only string as empty (leading/trailing whitespace on a non-empty value is preserved). The ZodString.Required fluent method is a covariant convenience over the generic rule.
AllowedValuesRule<T> and DeniedValuesRule<T> accept both a single value (new AllowedValuesRule<int>(5)) and an array. The generated [AllowedValuesZod] / [DeniedValuesZod] attributes surface the array as a params object[] (plus a single-value overload), so [AllowedValuesZod("a", "b", "c")] works on a string member and [DeniedValuesZod(1, 2, 3)] on an int member; the resolver converts each element to the member type.
Enum rules
Section titled “Enum rules”| Rule | Constructor | Fluent method | Code | Message format |
|---|---|---|---|---|
EnumRule<TEnum> | () or (TEnum[]? disallowed, string? message, string? code) | Enum() | invalid_enum_value | '{0}' is not a defined member of {1} |
EnumRule<TEnum> is generic over TEnum : struct, Enum and rejects a value that is not a defined member of the enum type, or that resolves to a member the rule excludes. It is equivalent to Zod’s z.nativeEnum(Enum) semantics. The defined members are resolved once per closed generic type, so the validation path performs no reflection.
The [ZodSchema] generator emits this rule automatically for every non-flags enum property — see Source Generator. The rule is closed with the property’s enum type and receives the excluded members as its disallowed set:
- every enum member marked
[ZodIgnore](a member that is defined but never a valid value, such asUnspecified), and - the values the property’s
[DeniedValues]attribute lists.
using System.ComponentModel.DataAnnotations;using ZodSharp;
public enum ExampleEnum{ [ZodIgnore] Unspecified,
AValidValue,
AnotherValidValue,}
[ZodSchema]public class Model{ public ExampleEnum Status { get; set; }
[DeniedValues(ExampleEnum.AnotherValidValue)] public ExampleEnum SecondaryStatus { get; set; }}The rule is skipped for a [Flags] enum (a combination is valid without being a defined member), for a property that declares an explicit [AllowedValues] allow-list (the allow-list governs), and when the schema opts out with [ZodSchema(ValidateEnumValues = false)]. A hand-written ZodNativeEnum<TEnum> schema already rejects undefined members; its Enum() method adds this rule to customise the message/code, and the rule can be attached to any enum-output schema with AddRule/Rule.
Generated attributes
Section titled “Generated attributes”Each built-in rule that can be expressed as an attribute ships a generated ValidationAttribute in the ZodSharp.Rules namespace (inside the Purview.ZodSharp assembly), so a member or a scalar can be annotated directly:
| Attribute | Rule | Notes |
|---|---|---|
[Email] | EmailRule | |
[E164] | E164Rule | |
[Regex("…")] | RegexRule | mirrors the (string pattern, string? message) overload |
[UUID] / [UUID(UuidVersion.…)] | UUIDRule | the version is optional; omit it for the versionless rule |
[ULID] | ULIDRule | |
[JWT] | JWTRule | |
[IPAddress] / [IPAddress(IPAddressRuleType.…)] | IPAddressRule | defaults to Any; pass a type to require IPv4/IPv6 |
[Hex] | HexRule | |
[Base64Url] | Base64UrlRule | |
[Nanoid] | NanoidRule | |
[Cuid2] | Cuid2Rule | |
[DateString] | DateStringRule | |
[TimeString] | TimeStringRule | |
[DatetimeString] | DatetimeStringRule | |
[StartsWith("…")] | StartsWithRule | |
[EndsWith("…")] | EndsWithRule | |
[Includes("…")] | IncludesRule | |
[MultipleOf(…)] | MultipleOfRule<T> | closes the open generic with the member type |
[Finite] | FiniteRule<T> | closes the open generic with the member type |
[SafeInteger] | SafeIntegerRule | |
[Int] | IntRule<T> | closes the open generic with the member type |
[Uri(UriKind.…)] | UriRule | the UriKind is required |
[NonSentinel] | NonSentinelRule<T> | closes the open generic with the member/scalar type |
[NullOrNonWhiteSpace] | NullOrNonWhiteSpaceRule | accepts null but rejects empty/whitespace-only strings (for nullable scalar value objects) |
[Enum] | EnumRule<TEnum> | closes the open generic with the enum member type; excludes only the members the rule receives |
[MinValue(…)] | MinValueRule<T> | the type-parameter bound is a double; the rule closes with the member type |
[MaxValue(…)] | MaxValueRule<T> | as above |
[GreaterThan(…)] | GreaterThanRule<T> | as above |
[LessThan(…)] | LessThanRule<T> | as above |
[GreaterThanOrEqual(…)] | GreaterThanOrEqualRule<T> | as above |
[LessThanOrEqual(…)] | LessThanOrEqualRule<T> | as above |
[Even] | EvenRule<T> | closes the open generic with the member type |
[Odd] | OddRule<T> | closes the open generic with the member type |
[MinLengthZod(…)] | MinLengthRule | suffixed to avoid the DataAnnotations name; exposes Code/Message |
[MaxLengthZod(…)] | MaxLengthRule | as above |
[UrlZod] | UrlRule | as above |
[PhoneZod] | PhoneRule | as above |
[CreditCardZod] | CreditCardRule | as above |
[Base64StringZod] | Base64StringRule | as above |
[Emoji] | EmojiRule | |
[Xid] | XidRule | |
[Ksuid] | KsuidRule | |
[Duration] | DurationRule | |
[Guid] | GuidRule | accepts any 8-4-4-4-12 hex identifier |
[Cidr] / [Cidr(CidrRuleType.…)] | CidrRule | defaults to Any; pass a type to require IPv4/IPv6 |
[RequiredZod(…)] | RequiredRule<T> | suffixed; exposes AllowEmptyStrings/TrimWhitespace and rejects an absent value exactly like [Required] |
[RangeZod(…)] | RangeRule<T> | the type-parameter bounds are doubles; the rule closes with the member type |
[LengthZod(…)] | LengthRule | suffixed to avoid the DataAnnotations name |
[StringLengthZod(…)] | StringLengthRule | as above |
[CompareZod(…)] | CompareRule | as above |
[AllowedValuesZod(…)] | AllowedValuesRule<T> | a params object[] array (and a single-value overload); the rule closes with the member type |
[DeniedValuesZod(…)] | DeniedValuesRule<T> | a params object[] array (and a single-value overload); the rule closes with the member type |
A generated attribute’s constructor mirrors the rule’s value parameters: a parameter the rule declares without a default becomes a required constructor argument, and a parameter with a default keeps that default (so an optional value such as [StartsWith("…", StringComparison.OrdinalIgnoreCase)] still has a usable default). The value parameters are also exposed as settable properties. The rule’s message and identity (code/origin) parameters stay properties only — Message and, where present, Code — and default to the rule’s own MessageFormat/ErrorCode, so the attribute is self-describing.
Each generated attribute reports the rule’s own ErrorCode/Origin because every built-in rule implements IZodRule.
Rules whose derived attribute name is already taken by System.ComponentModel.DataAnnotations — MinLengthRule, MaxLengthRule, UrlRule, PhoneRule, CreditCardRule, Base64StringRule, RequiredRule, RangeRule, LengthRule, StringLengthRule, CompareRule, AllowedValuesRule, DeniedValuesRule — are emitted under a Zod suffix instead (for example [MinLengthZod], [RequiredZod], [RangeZod]), so the rule’s own identity and message stay usable. The generic bound rules surface their type-parameter bound as a double, so [MinValue(3)] works on an int or a double member (the value is converted to the member type).
Exposed as DataAnnotations attributes
Section titled “Exposed as DataAnnotations attributes”The [ZodSchema] generator maps several built-in rules to their System.ComponentModel.DataAnnotations attributes, and any custom rule can be mapped with [ZodRule(typeof(...))]:
| Attribute | Rule | Failure code |
|---|---|---|
[EmailAddress] | EmailRule | invalid_string |
[Url] | UrlRule | invalid_string |
[Phone] | PhoneRule | invalid_string |
[CreditCard] | CreditCardRule | invalid_string |
[Base64String] | Base64StringRule | invalid_string |
[RegularExpression] | compiled Regex, not RegexRule | invalid_string |
Size and range attributes ([Length], [StringLength], [MinLength], [MaxLength], [Range]) are emitted as direct, typed codegen; the equivalent rule structs (LengthRule, StringLengthRule, MinLengthRule, MaxLengthRule, RangeRule<T>) are also available directly and through their generated attributes. The rule-identity variants of the colliding names are the suffixed generated attributes ([MinLengthZod], [MaxLengthZod], [UrlZod], [PhoneZod], [CreditCardZod], [Base64StringZod], [RequiredZod], [RangeZod], [LengthZod], [StringLengthZod], [CompareZod], [AllowedValuesZod], [DeniedValuesZod]), which report the rule’s own Code and accept a Message. The remaining built-in rules are covered by their own generated attributes — for example [Regex("…")] validates through RegexRule, whereas [RegularExpression] compiles the pattern inline. See Source Generator DataAnnotations for the full attribute table and the generated metadata, and Custom Rules for mapping your own rules to attributes.
Related
Section titled “Related”- Custom Rules — the
IValidationRule<T>contract,IZodRule, and attribute mapping. - String Validation — the
ZodStringfluent surface and span validation. - Number Validation — the
ZodNumberfluent surface. - Source Generator DataAnnotations — attributes the generator understands.
- Source Generator Diagnostics —
ZODSGEN042and the rule conventions analyzer.