JSON Schema Draft 7 to 2020-12 transition: unevaluatedProperties behavior
0 reputation · 23 Nov 2021, 21:10 UTC
0 reputation · 23 Nov 2021, 21:10 UTC
additionalProperties to unevaluatedProperties to maintain complex compositions. While Draft 7 relies on simple key-level restrictions, the Draft 2020-12 specification introduces unevaluatedProperties to allow for more granular recursive validation across sub-schemas.
When performing a zero-downtime migration, the application must handle data against both legacy and modern schemas. The primary challenge arises when unevaluatedProperties is used within nested allOf blocks. Because this keyword must track which keys have been evaluated by all sub-schemas to determine validity, implementation details vary across validators.
How does the unevaluatedProperties keyword identify valid keys when defined across multiple branches of an allOf composition, and are there known edge cases where this validation fails when transitioning from Draft 7 to 2020-12 logic?
35275 reputation · 24 Nov 2021, 04:59 UTC
In JSON Schema Draft 2020-12, unevaluatedProperties identifies valid keys by maintaining a set of 'evaluated' keys across the schema tree. Unlike Draft 7's additionalProperties, which only looks at the immediate object-level keys, unevaluatedProperties tracks whether a key has been validated by any sub-schema within a composition.
When a validator encounters an allOf block, it performs a union operation on all keys validated by each sub-schema. If sub-schema A validates property id and sub-schema B validates property name, both keys are marked as evaluated. If an unevaluatedProperties keyword is defined within or outside that allOf, it will only flag keys present in the instance that were not part of that union.
properties, patternProperties, additionalProperties, or defs.allOf, the evaluation is additive. If any branch validates a key, that key is no longer "unevaluated" for the remainder of the validation process.Transitioning from Draft 7 to 2020-12 often fails when developers rely on the restrictive nature of additionalProperties: false. Common issues include:
additionalProperties: false inside an allOf branch only restricts keys not defined in that specific branch. In 2020-12, using unevaluatedProperties there might allow unexpected keys if a sibling branch in the same allOf hasn't explicitly validated them yet.$ref to point to itself, the validator must correctly track the evaluation state across the recursive boundary. Some early implementations reset the evaluation set, causing unevaluatedProperties to incorrectly flag valid keys.patternNames with complex regex can mark keys as evaluated. If the regex is too broad, it may "consume" keys that you intended for unevaluatedProperties to catch.To ensure a safe migration, verify if your chosen validator fully supports the Draft 2020-12 specification's evaluation tracking, as many libraries still default to Draft 7-style key-level restrictions for compatibility.
Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 23 Nov 2021, 23:33 UTC
A critical nuance when migrating to Draft 2020-12 is the interaction between allOf failure and the evaluation state. In most compliant validators (such as Ajv 8.x in 2020-12 mode), if any sub-schema within an allOf block fails, the overall validation for that instance fails immediately.
This creates a specific edge case: unevaluatedProperties is only applied after all sub-schemas have been successfully processed. If a preceding branch in the allOf composition fails, the validator may not reach the logic that marks certain keys as "evaluated." Consequently, you cannot rely on unevaluatedProperties to provide a fallback or a "catch-all" validation if the primary structural constraints of the allOf branches are not met first.
To verify this behavior during migration, test a schema where one allOf branch contains a type mismatch and another contains the property definition. You will likely find that the validation error triggers on the type mismatch before the unevaluatedProperties logic even executes.