2026-09-20 · 6 min read · Rahul Chitturi
- json-schema
- validation
- api
The contains keyword asks whether array elements match a subschema. In newer JSON Schema dialects, minContains and maxContains constrain how many elements match, which is different from constraining the total array length.
Confusing contains with items or minItems produces schemas that accept the wrong arrays. Results also diverge when a validator uses an older dialect that does not implement the cardinality keywords.
Count matching indexes explicitly
For a failing fixture, evaluate the contains subschema against each element and record the matching indexes. Then apply the active dialect's minContains and maxContains semantics to that match count.
- Declare the intended schema dialect with $schema.
- Keep total length rules in minItems and maxItems.
- Use uniqueItems only for whole-value uniqueness.
- Check how the validator reports unsupported keywords.
Watch composition and annotations
An element may satisfy several branches inside contains, but it still occupies one matching array index for that contains evaluation. Nested allOf, anyOf, conditionals, and references can make the match reason difficult to see without isolated tests.
Evaluation annotations can also interact with unevaluatedItems in supported dialects. Validate the complete schema with the production validator rather than reproducing only the final count in application code.
Create cardinality boundary fixtures
Use Flashman's JSON formatter for schemas and instances, diff for nearly identical fixtures, regex tester for pattern-based subschemas, and number-base converter for numeric boundary examples.
Test empty arrays, zero matches, exactly the minimum and maximum, one beyond each boundary, duplicate values, elements matching multiple branches, unresolved references, omitted cardinality keywords, draft changes, and validator upgrades.