Conversation
- Add $vocabulary support for vocabulary-based keyword filtering - Implement $recursiveRef and $recursiveAnchor resolution - Add duration and uuid format validators - Fix $ref resolution with embedded $id schemas - Improve unevaluatedProperties handling with dependentSchemas - Support scope isolation for $ref with sibling keywords
|
cc @aeschli |
There was a problem hiding this comment.
Looks good. This is a huge step toward being fully compliant with the JSON Schema spec, but there's definitely a few pieces still missing. I expect that this isn't intended to implement everything, but it's way closer than what we have now.
The following list is all the things I tested. I tested it with these changes running in vscode. Most of the issues are related to identifying which keywords should be recognized/ignored depending on the version of JSON Schema in use. It respects all implemented keywords regardless of what version of JSON Schema the schema uses. That didn't surprise me. I know this implementations doesn't make those distinctions, but I did notice in the code that you tried to add support for those things by implementing the $vocabulary keyword. However, I didn't see any indication that it was working at all. I see there are tests, but when I try those same scenarios in vscode, I don't see $vocabulary being respected.
The other thing I noticed is that in 2019-09, the format keyword is supposed to be an annotation only. You're allowed to provide a way to enable format validation, but it's not supposed to be enabled by default. I understand if the intention is enable this by default, but technically that's not correct.
In 2019-09, you can also enable/disable format using $vocabulary. false means annotation-only and true means validate. This can be overridden by configuration, but those are the defaults. It would be nice to see that working fully.
- ❌ Relative reference with
$id- This has never worked properly, not just with 2019-09 changes. I'm sure it wasn't intended for this to be in scope for these changes, but it would be nice to get this working properly at some point. I don't think it would be too hard compared to everything else you've achieved in this PR.
- ✔️ Reference an
$anchorin the same schema - ✔️ Reference an
$anchorin an external schema - ✔️ Reference an
$anchorin an embedded schema - ❌
$idfragments aren't anchors in a 2020-12 schema - ❌
$anchorisn't an anchor in a draft-07/6/4 schema - ✔️ embedded schemas
- ✔️ nested embedded schemas
- ✔️ recursive references
- ✔️
$refwith siblings - ✔️
dependentRequiredworks in 2019-09 - ❌
dependentRequiredshouldn't work in draft-07 - ❌
dependentSchemasworks in 2019-09- Works, but the error is on the wrong node. It appears on the property key, not the property value.
- ❌
dependentSchemasshouldn't work in draft-07 - ❌
dependenciesshouldn't work in 2019-09 - ✔️
unevaluatedItems - ✔️
unevaluatedProperties - ✔️
unevalautedPropertieswithdependentSchemas - ✔️
minContains/maxContains - ❌
formatdoesn't validate by default - ❌ Enable format validation using
$vocabulary - ❌ Disable vocab using
$vocabulary
One more thing. I'm not sure if there was something missing in my testing setup or what, but I wasn't seeing vscode recognize the changes in my schemas while editing. I had to refresh (Ctrl+R) in order for the schema changes to be recognized in the JSON document that linked it. If that's related to these changes, that could be a concern, but I expect it's just my setup.
Thanks for this work! It will be huge for JSON Schema to get this and 2020-12 support in vscode.
…2019-09 - Change Vocabularies type from Set to Map to track required/optional status - Add isFormatAssertionEnabled for format-annotation vs format-assertion - Handle $ref sibling keywords correctly per draft version - Only recognize $anchor in draft-2019-09 and later schemas - Fix dependencies keyword to only apply in draft-07 and earlier - Fix missing property error location to use object offset
|
|
I think there was some confusion because I wasn't clear enough in my checklist. I haven't had a chance to test this round of changes yet, but I want to make sure there aren't any miscommunications.
That's correct.
|
I'm still seeing {
"$schema": "./my-schema.json",
"date": "not a date" // <-- Should be ok, but has a validation error
}{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "object",
"properties": {
"date": {
"type": "string",
"format": "date" // <-- Should be annotation-only in 2019-09
}
}
}
Mostly looks right except for one case. {
"$schema": "./my-schema.json",
"foo": 42 // <-- This is showing that the value should be a string, but the reference shouldn't have worked.
}{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "object",
"properties": {
"foo": { "$ref": "#foo" } // <-- The reference target shouldn't exist
},
"$defs": {
"": {
"$id": "#foo", // <-- Should not create an anchor in draft-2019-09
"type": "string"
}
}
}
I think this is working better. I'm seeing draft-2019-09 schemas allowing only draft-2019-09 keywords. But, in draft-07 schemas, all keywords seem to work including draft-2019-09 keywords. Also, the vocabulary filtering only seems to work for built-in schemas like draft-2019-09. I'm not seeing it work for custom meta-schemas. Not sure if that was intended or not, but it looks like you wrote tests for it so I expected it to work. {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$vocabulary": {
"https://json-schema.org/draft/2019-09/vocab/core": true,
"https://json-schema.org/draft/2019-09/vocab/applicator": true
},
"$ref": "https://json-schema.org/draft/2019-09/schema"
}{
"$schema": "./my-dialect.json",
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "minimum": 0 } // <-- "minimum" should do nothing
},
"required": ["name"] // <-- "required" should do nothing
}{ // <-- I'm seeing an error from "required" for "name" not being included, but "required" should be ignored.
"$schema": "./my-schema.json",
"age": -3 // <-- I'm seeing an error from "minimum" that should be ignored
}My checklist so far:
|
Overview
This PR adds further support for JSON Schema draft 2019-09.
The changes focus on three main areas:
$recursiveRefresolution$refhandling with embedded schemasThis is a very large change, happy to break it up however would be easiest to review.
Key Changes
1. Vocabulary Support (
$vocabulary)Files:
vocabularies.ts(new),jsonSchemaService.ts,jsonParser.ts,jsonLanguageTypes.tsJSON Schema 2019-09 introduced vocabularies, which allow meta-schemas to declare which keyword sets are active. This enables custom meta-schemas to disable certain validation keywords.
Implementation
Vocabulariestype (Set<string>) to represent active vocabulary URIsisKeywordEnabled()function that checks if a keyword should be processed based on active vocabularies$schema) and extracts$vocabularydeclarationsenabled(keyword)before processing each keyword$id,$ref,$schema, etc.) are always enabled per the specExample
A meta-schema with only
applicatorvocabulary (novalidationvocabulary) will skipminimum,required,type, etc.2.
$recursiveRefand$recursiveAnchorSupportFiles:
jsonParser.ts,jsonSchema.tsThese keywords (2019-09) enable extensible recursive schemas—a pattern where a base schema can be extended while maintaining recursion to the extending schema.
Recursive Ref Implementation
schemaStackandschemaRootsparameters to thevalidate()function to track schema traversal$recursiveRefis encountered:$id)$recursiveAnchor: true, find the first$recursiveAnchor: truein the stack$recursiveAnchortype fromstringtoboolean | stringto handle both spec-correct and real-world schemas3. Improved
$refResolution with Embedded$idSchemasFiles:
jsonSchemaService.tsSchemas can contain embedded sub-schemas with their own
$id, creating new URI scopes. This PR properly resolves$refagainst the correct base URI.Key Fixes
traverseWithBaseTracking()$idvaluesregisterEmbeddedSchemas()collectAnchors()$idboundaries since anchors are scoped to their document4. Scope Isolation for
$refwith Sibling KeywordsFiles:
jsonSchemaService.tsIn 2019-09+,
$refcan have sibling keywords, butunevaluatedProperties/unevaluatedItemsin the referenced schema shouldn't see properties evaluated by those siblings.Scope Isolation Implementation
needsScopeIsolation()to detect when isolation is neededallOf:{ "allOf": [ { /* $ref'd schema */ }, { /* sibling keywords */ } ] }5.
dependentSchemasIntegration withunevaluatedPropertiesFiles:
jsonParser.tsProperties validated by
dependentSchemaswere not being tracked as "evaluated," causing false positives withunevaluatedProperties: false.Fix: Added
validationResult.mergeProcessedProperties()after validatingdependentSchemas.6.
patternPropertiesHandling FixFiles:
jsonParser.tsPreviously,
patternPropertieswas processed incrementally, which caused issues when a property matched multiple patterns.Fix: Collect all pattern matches first, validate against all matching patterns, then mark as processed.
7. New Format Validators
Files:
jsonParser.tsAdded validators for two new 2019-09 formats:
durationP1Y2M3DT4H5M6Suuid550e8400-e29b-41d4-a716-4466554400008. Type Definition Updates
Files:
jsonSchema.ts$recursiveAnchorstringboolean | string$vocabularyany{ [uri: string]: boolean }Testing
New Test Files
vocabularies.test.ts: Unit tests forisKeywordEnabled()covering all vocabulary combinationsUpdated Test Files
parser.test.ts: Added tests for:$recursiveRefdependentSchemas+unevaluatedPropertiesdurationanduuidformatsschema.test.ts: Added tests for:jsonSchemaTestSuite.test.ts:Tests Now Passing
The following test categories now pass:
$idresolutionpatternPropertiesunevaluatedProperties$recursiveRefUnsupported Features
The following 2020-12 features are detected and produce warnings:
$dynamicRef$dynamicAnchorBreaking Changes
None. All changes are backward compatible.
Remaining Skipped Tests
Most remaining skipped tests fall into two categories:
localhost:1234(partially addressed)$dynamicRef/$dynamicAnchor: 2020-12 features not yet implemented