Skip to content

Support JSON Schema 2019 and 2020#1166

Merged
datho7561 merged 35 commits intoredhat-developer:mainfrom
shin19991207:chang-patch-2019-2020
Feb 5, 2026
Merged

Support JSON Schema 2019 and 2020#1166
datho7561 merged 35 commits intoredhat-developer:mainfrom
shin19991207:chang-patch-2019-2020

Conversation

@shin19991207
Copy link
Member

@shin19991207 shin19991207 commented Jan 16, 2026

What does this PR do?

Changes

Refactor / architecture

  • Extract instance validation into a validator layer (base + draft-specific validators)
  • Keep backwards compatibility where reasonable (draft-07 behavior remains the baseline)

Draft 2019-09 support

keyword/item implementation tests note
$anchor resolution ✅ from JSON Schema Test Suite Draft-07 and earlier should resolve plain‑name fragments defined by $id, see Additional fixes2
$defs resolution (renamed + definitions compat) - - already covered (it resolved to the same way as definitions)
$id - - covered by meta-validation1
$recursiveAnchor / $recursiveRef ✅ from JSON Schema Test Suite
$ref resolution updated to support sibling keywords ✅ from JSON Schema Test Suite Draft-07 and earlier should ignore siblings, see Additional fixes1
$vocabulary - - covered by meta-validation1
dependentSchemas ✅ from JSON Schema Test Suite
unevaluatedItems ✅ from JSON Schema Test Suite
unevaluatedProperties ✅ from JSON Schema Test Suite
dependentRequired ✅ from JSON Schema Test Suite
contains + minContains / maxContains ✅ from JSON Schema Test Suite

Draft 2020-12 support

keyword/item implementation tests note
prefixItems + items ✅ from JSON Schema Test Suite
$dynamicAnchor / $dynamicRef ✅ from JSON Schema Test Suite
contains and unevaluatedItems ✅ from JSON Schema Test Suite
Compound Schema Document "In Draft 2019-09, the meaning of $id in a sub-schema changed from indicating a base URI change within the current schema to indicating an embedded schema independent of the parent schema (ref)", see Additional fixes2
Regular Expressions - - covered by safeCreateUnicodeRegExp()
Media Type Changes - - not related to validation

For Compound Schema Document:

  • Add meta‑validation for mixed‑dialect schemas
    • each schema node with a $schema field is validated against its own dialect’s meta‑schema; mixed‑dialect schemas are cloned with subschemas replaced by {} at dialect boundaries to prevent validation conflicts
  • Add instance‑validation support for mixed‑dialect schemas

Additional fixes

  1. The current implementation of $ref resolution is incorrect, as sibling keywords are not being ignored as required by Draft‑07 (and earlier). According to the JSON Schema Draft‑07 specification (Section 8.3), "An object schema with a "$ref" property MUST be interpreted as a "$ref" reference. ... All other properties in a "$ref" object MUST be ignored."

    Using the following schema:

    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "type": "object",
      "properties": {
        "value": {
          "$ref": "#/definitions/A",
          "type": "number"
        }
      },
      "required": ["value"],
      "definitions": {
        "A": { "type": "string" }
      }
    }

    Draft‑07 requires the "type": "number" keyword to be ignored, because it is a sibling of $ref. Validation should therefore use only the referenced schema { "type": "string" }.

    Expected results:

    • value: "hello" => valid
    • value: 1 => Incorrect type. Expected "string".

    However, the current implementation produces:

    • value: "hello" => Incorrect type. Expected "number".
    • value: 1 => valid

    This PR updates the behavior to correctly ignore sibling keywords and match the expected Draft‑07 semantics.
    This should fix Rejects schemas where $ref refers to $id or $anchor #823.

  2. Fixes base‑URI/$id resolution:

    • resolve relative $ref against the nearest $id (root + subschema) for all Draft versions
    • resolve plain‑name fragments defined by $id for Draft‑07 and earlier
    • treat $id without a fragment as an embedded resource (e.g. other.json) instead of loading it externally for all Draft versions (note: this behaviour is officially defined for Draft-2019+ but is harmless to have for Draft-07 and earlier)

    Example 1: root $id changes base for relative $ref

    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "$id": "https://www.schemastore.org/",
      "type": "object",
      "properties": { "pkg": { "$ref": "package.json" } }
    }
    # pkg: 123

    Before fix: ref is resolved against the retrieval URL file:///…/package.json and gives “Problems loading reference...” error
    After fix: use base from $id, so it resolves to https://www.schemastore.org/package.json and validates pkg.

    Example 2: plain‑name fragment from $id

    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "definitions": {
        "Name": { "$id": "#Thing", "type": "string", "minLength": 2 }
      },
      "$ref": "#Thing"
    }
    # A

    Before fix: #Thing is treated as an unresolved $ref and gives “$ref 'Thing' … cannot be resolved” error.
    After fix: #Thing resolves to definitions.Name and validates minLength

    Example 3: embedded resource $id

    {
      "$schema": "https://json-schema.org/draft/2019-09/schema",
      "type": "object",
      "properties": { "x": { "$ref": "other.json#bar" } },
      "required": ["x"],
      "defs": {
        "B": {
          "$id": "other.json",
          "$defs": { "X": { "$anchor": "bar", "type": "string", "minLength": 2 } }
        }
      }
    }
    # x: A

    Before fix: attempts to fetch other.json externally and gives "Problems loading reference..." error
    After fix: resolves to the embedded resource and validates minLength

What issues does this PR fix or reference?

#856
#1112

Should also:

Is it tested? How?

Footnotes

  1. Meta‑validation support for Draft‑2019/2020 was added in PR https://github.com/redhat-developer/yaml-language-server/pull/1164 2

// eslint-disable-next-line @typescript-eslint/no-unused-vars
_node: ObjectASTNode,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_schema: JSONSchema,

Check warning

Code scanning / ESLint

Disallow unused variables Warning

'_schema' is defined but never used.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_validationResult: ValidationResult,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_matchingSchemas: ISchemaCollector,

Check warning

Code scanning / ESLint

Disallow unused variables Warning

'_matchingSchemas' is defined but never used.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_matchingSchemas: ISchemaCollector,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_options: Options

Check warning

Code scanning / ESLint

Disallow unused variables Warning

'_options' is defined but never used.

const propertyNode = child.type === 'property' ? child : (child.parent as ASTNode);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const keyNode = (propertyNode as any).keyNode;

Check warning

Code scanning / ESLint

Disallow the `any` type Warning

Unexpected any. Specify a different type.
@coveralls
Copy link

coveralls commented Jan 17, 2026

Coverage Status

coverage: 84.605% (+2.0%) from 82.644%
when pulling 9f79afc on shin19991207:chang-patch-2019-2020
into d74612c on redhat-developer:main.

Signed-off-by: Morgan Chang <shin19991207@gmail.com>
Signed-off-by: Morgan Chang <shin19991207@gmail.com>
…pendencies for backward compatibility

Signed-off-by: Morgan Chang <shin19991207@gmail.com>
… to contains and unevaluatedItems

Signed-off-by: Morgan Chang <shin19991207@gmail.com>
@shin19991207 shin19991207 force-pushed the chang-patch-2019-2020 branch from 2aa38a8 to 9d322fa Compare January 20, 2026 19:22
Update baseValidator (split out from the original jsonParser07)
updated $ref resolution to support sibling keywords for Draft-2019+ and fixed $ref resolution bug for Draft-07 and earlier
Signed-off-by: Morgan Chang <shin19991207@gmail.com>
…ma Document

Signed-off-by: Morgan Chang <shin19991207@gmail.com>
parentSchemaDependencies: SchemaDependencies,
resolveRefDependencies: SchemaDependencies
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise<any> => {

Check warning

Code scanning / ESLint

Disallow the `any` type Warning

Unexpected any. Specify a different type.
Signed-off-by: Morgan Chang <shin19991207@gmail.com>
Signed-off-by: Morgan Chang <shin19991207@gmail.com>
…pendencies for backward compatibility

Signed-off-by: Morgan Chang <shin19991207@gmail.com>
… to contains and unevaluatedItems

Signed-off-by: Morgan Chang <shin19991207@gmail.com>
Update baseValidator (split out from the original jsonParser07)
updated $ref resolution to support sibling keywords for Draft-2019+ and fixed $ref resolution bug for Draft-07 and earlier
Signed-off-by: Morgan Chang <shin19991207@gmail.com>
…ma Document

Signed-off-by: Morgan Chang <shin19991207@gmail.com>
Signed-off-by: Morgan Chang <shin19991207@gmail.com>
Signed-off-by: Morgan Chang <shin19991207@gmail.com>
Signed-off-by: Morgan Chang <shin19991207@gmail.com>
Signed-off-by: Morgan Chang <shin19991207@gmail.com>
@shin19991207 shin19991207 marked this pull request as ready for review February 2, 2026 14:17
@shin19991207 shin19991207 changed the title [WIP] Support JSON Schema 2019 and 2020 Support JSON Schema 2019 and 2020 Feb 2, 2026
@shin19991207 shin19991207 moved this from In Progress to Pending review in Java Tooling Feb 2, 2026
…ans on the schema and use a post‑order pass in _indexSchemaResources for meta-validation instead

Signed-off-by: Morgan Chang <shin19991207@gmail.com>
Signed-off-by: Morgan Chang <shin19991207@gmail.com>
@datho7561
Copy link
Contributor

I'm trying out a bunch of the schemas from this repo: https://github.com/json-schema-org/JSON-Schema-Test-Suite

This case doesn't seem to work properly: https://github.com/json-schema-org/JSON-Schema-Test-Suite/blob/main/tests/draft2019-09/recursiveRef.json#L319

@datho7561
Copy link
Contributor

This one too: https://github.com/json-schema-org/JSON-Schema-Test-Suite/blob/main/tests/draft2019-09/unevaluatedItems.json#L102

@datho7561
Copy link
Contributor

datho7561 commented Feb 2, 2026

@shin19991207
Copy link
Member Author

@datho7561 Thanks for catching those! I only discovered the JSON‑Schema‑Test‑Suite repo last week, so I had only added the recursive/dynamic reference tests. I’ll pull in more automated tests from it ....

Signed-off-by: Morgan Chang <shin19991207@gmail.com>
Signed-off-by: Morgan Chang <shin19991207@gmail.com>
…rg/JSON-Schema-Test-Suite

Signed-off-by: Morgan Chang <shin19991207@gmail.com>
Signed-off-by: Morgan Chang <shin19991207@gmail.com>
import type { ASTNode } from '../jsonASTTypes';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function getNodeValue(node: ASTNode): any {

Check warning

Code scanning / ESLint

Disallow the `any` type Warning

Unexpected any. Specify a different type.
Copy link
Contributor

@datho7561 datho7561 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works well, it's well tested, and the refactoring seems to have cleaned up the code a lot. Thanks, Morgan! Well done!

@datho7561 datho7561 merged commit d22a9d9 into redhat-developer:main Feb 5, 2026
4 checks passed
@github-project-automation github-project-automation bot moved this from Pending review to Done in Java Tooling Feb 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

3 participants