You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
JSON Schema provides conditional validation keywords that allow the schema to adapt based on the input data:
if: Specifies a subschema to test against the instance
then: Applied when the instance matches the if subschema
else: Applied when the instance doesn't match the if subschema
These keywords enable more complex validation logic and are part of the JSON Schema 2020-12 standard. Currently, the @typespec/json-schema package doesn't provide explicit decorators for these keywords, but they can be added manually via the generic @extension decorator.
Proposal
Add three new decorators to the TypeSpec JSON Schema library:
/** * Specifies a JSON Schema conditional validation. When the given schema matches * the instance, the schema in the corresponding `@then` decorator is applied. * Otherwise, the schema in the corresponding `@else` decorator (if present) is applied. * * The schema can be provided as a model reference or as an object value using the `#{}` syntax. * * @param schema The schema to test against the instance, either a model reference or an object value */externdecif(target: Model|Scalar|Enum|Union|Reflection.ModelProperty,schema: unknown|valueofobject);/** * Specifies a JSON Schema that applies when the corresponding `@if` schema matches. * Must be used in conjunction with the `@if` decorator on the same target. * * The schema can be provided as a model reference or as an object value using the `#{}` syntax. * * @param schema The schema to apply when the condition matches, either a model reference or an object value */externdecthen(target: Model|Scalar|Enum|Union|Reflection.ModelProperty,schema: unknown |valueofobject);/** * Specifies a JSON Schema that applies when the corresponding `@if` schema doesn't match. * Must be used in conjunction with the `@if` decorator on the same target. * * The schema can be provided as a model reference or as an object value using the `#{}` syntax. * * @param schema The schema to apply when the condition doesn't match, either a model reference or an object value */externdecelse(target: Model|Scalar|Enum|Union|Reflection.ModelProperty,schema: unknown |valueofobject);
These decorators would allow direct and intuitive specification of conditional schemas in TypeSpec.
Examples
Basic Conditional Validation
modelContact{// Validate email format only if type is "email"
@if(#{properties: #{type: #{const: "email"}}})
@then(#{format: "email"})
value: string;
type: "email"|"phone";}
Using Both Then and Else
modelVerificationCode{// Different pattern validation based on format
@if(#{properties: #{format: #{const: "numeric"}}})
@then(#{pattern: "^[0-9]{6}$"})
@else(#{pattern: "^[A-Za-z0-9]{8}$"})
code: string;
format: "numeric"|"alphanumeric";}
Using Model References
// Define reusable validation patternsmodelEmailPattern{
pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";}modelPhonePattern{
pattern: "^\\+[0-9]{1,3}\\s[0-9]{9,15}$";}modelFormattedContact{// Apply appropriate pattern based on contact type
@if(#{properties: #{type: #{const: "email"}}})
@then(EmailPattern)
@else(PhonePattern)
value: string;
type: "email"|"phone";}
exportconst$lib=createTypeSpecLibrary({// ... existing code ...state: {// ... existing state ..."JsonSchema.if": {description: "Contains data configured with @if decorator"},"JsonSchema.then": {description: "Contains data configured with @then decorator"},"JsonSchema.else": {description: "Contains data configured with @else decorator"},},}asconst);
Decorator Implementation
Add getters/setters in decorators.ts:
exportconst[/** Get schema set by `@if` decorator */getIf,setIf,/** {@inheritdoc IfDecorator} */$if,]=createDataDecorator<IfDecorator,Type|object>(JsonSchemaStateKeys["JsonSchema.if"]);exportconst[/** Get schema set by `@then` decorator */getThen,setThen,/** {@inheritdoc ThenDecorator} */$then,]=createDataDecorator<ThenDecorator,Type|object>(JsonSchemaStateKeys["JsonSchema.then"]);exportconst[/** Get schema set by `@else` decorator */getElse,setElse,/** {@inheritdoc ElseDecorator} */$else,]=createDataDecorator<ElseDecorator,Type|object>(JsonSchemaStateKeys["JsonSchema.else"]);
Schema Generation
Update #applyConstraints in json-schema-emitter.ts:
#applyConstraints(type: Scalar|Model|ModelProperty|Union|UnionVariant|Enum,schema: ObjectBuilder<unknown>,){// ... existing code ...// For handling conditional schemasconstapplyConditionalConstraint=(fn: (p: Program,t: Type)=>Type|object|undefined,key: string)=>{constconstraint=fn(this.emitter.getProgram(),type);if(constraint===undefined)return;if(isType(constraint)){// It's a TypeSpec model referenceconstref=this.emitter.emitTypeReference(constraint);compilerAssert(ref.kind==="code","Unexpected non-code result from emit reference");schema.set(key,ref.value);}else{// It's an object value provided with #{} syntaxschema.set(key,constraint);}};applyConditionalConstraint(getIf,"if");applyConditionalConstraint(getThen,"then");applyConditionalConstraint(getElse,"else");// ... remainder of existing code ...}
Decorator Validation
Add diagnostics code in lib.ts:
exportconst$lib=createTypeSpecLibrary({name: "@typespec/json-schema",diagnostics: {// ... existing diagnostics ..."then-without-if": {severity: "warning",messages: {default: paramMessage`@then decorator used without corresponding @if decorator on the same target`,},},"else-without-if": {severity: "warning",messages: {default: paramMessage`@else decorator used without corresponding @if decorator on the same target`,},},},// ... remainder of existing code ...}asconst);
The createDataDecorator function may need modification to properly handle both Type and object values. This implementation will need to ensure that both model references and object values are properly stored and retrieved.
2. Proper JSON Schema Generation
The implementation should ensure that JSON Schema conditionals are properly generated according to the JSON Schema specification. The 2020-12 version of JSON Schema is already used by the emitter.
3. Model Reference Resolution
When model references are used as schema values, they need to be properly resolved to JSON Schema. The existing type reference resolution logic should handle this correctly.
4. Nested Conditionals
While decorators can't be nested, the proposal supports complex conditions through model composition. This is a natural extension of TypeSpec's modeling capabilities.
Benefits
Direct Access to JSON Schema Conditionals: Provides explicit decorators for a powerful JSON Schema feature.
Improved Developer Experience: More intuitive and discoverable than using generic @extension.
Flexibility: Supports both simple inline conditions and complex scenarios through model composition.
Type Safety: Leverages TypeSpec's type system for schema validation.
Consistency: Follows the pattern of other JSON Schema feature decorators.
Limitations
No Direct Decorator Nesting: Complex conditions require model composition, which is more verbose than direct nesting.
Learning Curve: Developers need to understand how to compose models for complex conditional scenarios.
Decorator Co-dependency: @then and @else only make sense when used with @if, which creates a usage dependency.
JSON Schema Conditional Validation in TypeSpec
This proposal adds support for JSON Schema conditional validation (
if
/then
/else
) to the@typespec/json-schema
package.Background
JSON Schema provides conditional validation keywords that allow the schema to adapt based on the input data:
if
: Specifies a subschema to test against the instancethen
: Applied when the instance matches theif
subschemaelse
: Applied when the instance doesn't match theif
subschemaThese keywords enable more complex validation logic and are part of the JSON Schema 2020-12 standard. Currently, the
@typespec/json-schema
package doesn't provide explicit decorators for these keywords, but they can be added manually via the generic@extension
decorator.Proposal
Add three new decorators to the TypeSpec JSON Schema library:
These decorators would allow direct and intuitive specification of conditional schemas in TypeSpec.
Examples
Basic Conditional Validation
Using Both Then and Else
Using Model References
Complex Conditions with Model Composition
Constraining String Formats
Technical Implementation
Library State Keys
Add new state keys in
lib.ts
:Decorator Implementation
Add getters/setters in
decorators.ts
:Schema Generation
Update
#applyConstraints
injson-schema-emitter.ts
:Decorator Validation
Add diagnostics code in
lib.ts
:Implement validation in the decorator functions:
Alternative Approaches Considered
1. Single Conditional Decorator
Instead of three separate decorators, a single decorator could be used:
Pros:
@then
or@else
are used without@if
Cons:
2. Using Existing Extension Decorator
The existing
@extension
decorator could handle this without new decorators:Pros:
Cons:
Technical Considerations
1. State Data Types
The
createDataDecorator
function may need modification to properly handle bothType
andobject
values. This implementation will need to ensure that both model references and object values are properly stored and retrieved.2. Proper JSON Schema Generation
The implementation should ensure that JSON Schema conditionals are properly generated according to the JSON Schema specification. The 2020-12 version of JSON Schema is already used by the emitter.
3. Model Reference Resolution
When model references are used as schema values, they need to be properly resolved to JSON Schema. The existing type reference resolution logic should handle this correctly.
4. Nested Conditionals
While decorators can't be nested, the proposal supports complex conditions through model composition. This is a natural extension of TypeSpec's modeling capabilities.
Benefits
Direct Access to JSON Schema Conditionals: Provides explicit decorators for a powerful JSON Schema feature.
Improved Developer Experience: More intuitive and discoverable than using generic
@extension
.Flexibility: Supports both simple inline conditions and complex scenarios through model composition.
Type Safety: Leverages TypeSpec's type system for schema validation.
Consistency: Follows the pattern of other JSON Schema feature decorators.
Limitations
No Direct Decorator Nesting: Complex conditions require model composition, which is more verbose than direct nesting.
Learning Curve: Developers need to understand how to compose models for complex conditional scenarios.
Decorator Co-dependency:
@then
and@else
only make sense when used with@if
, which creates a usage dependency.Checklist
The text was updated successfully, but these errors were encountered: