Skip to content

Error validating a JSON Schema against formal draft JSON Schema meta data #475

@ericbroda

Description

@ericbroda

My objective is to validate a JSON Schema against JSON Schema meta-data (ie. use the JSON schema meta-data as the "grammar" to validate an actual JSON Schema)

The issue is that obvious errors are not caught:

  • changing the type "array" to "sdfsdfsd" - no error caught
  • changing a "$ref" to point to a non-existing definition - no error caught

I have provided several files below:

  • jsonschema.json: this is the combined meta-data files from json-schema.org
  • test.schema.json: this is the schema to be validated (against jsonschema.json)
  • cde fragment to execute the validation.

(In the code fragment (later), this is the file "test.schema.json")

{
  "$id": "https://example.com/arrays.schema.json",
  "description": "A representation of a person, company, organization, or place",
  "type": "object",
  "required": [ "messages" ],
  "properties": {
    "messages": {
      "type": "array",
      "items": { "$ref": "#/$defs/message" }
    }
  },
  "$defs": {
    "message": {
      "type": "object",
      "required": [ "key", "data" ],
      "properties": {
        "key": {
          "$ref": "#/$defs/key"
        },
        "data": {
          "$ref": "#/$defs/data"
        }
      }
    },
    "key": {
      "type": "string"
    },
    "data": {
      "type": "object",
      "required": [ "veggieName", "veggieLike" ],
      "properties": {
        "veggieName": {
          "type": "string",
          "description": "The name of the vegetable."
        },
        "veggieLike": {
          "type": "boolean",
          "description": "Do I like this vegetable?"
        }
      }
    }
  }
}

The JSON Schema meta-data is a consolidation of meta-data files from JSON-Schema.org (https://json-schema.org/specification.html):

(in the code below, this is "jsonschema.json)

{
    "$id": "https://json-schema.org/draft/2020-12/meta/validation",
    "$vocabulary": {
        "https://json-schema.org/draft/2020-12/vocab/validation": true
    },
    "$dynamicAnchor": "meta",

    "title": "Validation vocabulary meta-schema",
    "type": ["object", "boolean"],
    "properties": {
        // validation.json
        "type": {
            "anyOf": [
                { "$ref": "#/$defs/simpleTypes" },
                {
                    "type": "array",
                    "items": { "$ref": "#/$defs/simpleTypes" },
                    "minItems": 1,
                    "uniqueItems": true
                }
            ]
        },
        "const": true,
        "enum": {
            "type": "array",
            "items": true
        },
        "multipleOf": {
            "type": "number",
            "exclusiveMinimum": 0
        },
        "maximum": {
            "type": "number"
        },
        "exclusiveMaximum": {
            "type": "number"
        },
        "minimum": {
            "type": "number"
        },
        "exclusiveMinimum": {
            "type": "number"
        },
        "maxLength": { "$ref": "#/$defs/nonNegativeInteger" },
        "minLength": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
        "pattern": {
            "type": "string",
            "format": "regex"
        },
        "maxItems": { "$ref": "#/$defs/nonNegativeInteger" },
        "minItems": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
        "uniqueItems": {
            "type": "boolean",
            "default": false
        },
        "maxContains": { "$ref": "#/$defs/nonNegativeInteger" },
        "minContains": {
            "$ref": "#/$defs/nonNegativeInteger",
            "default": 1
        },
        "maxProperties": { "$ref": "#/$defs/nonNegativeInteger" },
        "minProperties": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
        "required": { "$ref": "#/$defs/stringArray" },
        "dependentRequired": {
            "type": "object",
            "additionalProperties": {
                "$ref": "#/$defs/stringArray"
            }
        },
        // applicator.json
        "prefixItems": { "$ref": "#/$defs/schemaArray" },
        "items": { "$dynamicRef": "#meta" },
        "contains": { "$dynamicRef": "#meta" },
        "additionalProperties": { "$dynamicRef": "#meta" },
        "properties": {
            "type": "object",
            "additionalProperties": { "$dynamicRef": "#meta" },
            "default": {}
        },
        "patternProperties": {
            "type": "object",
            "additionalProperties": { "$dynamicRef": "#meta" },
            "propertyNames": { "format": "regex" },
            "default": {}
        },
        "dependentSchemas": {
            "type": "object",
            "additionalProperties": { "$dynamicRef": "#meta" },
            "default": {}
        },
        "propertyNames": { "$dynamicRef": "#meta" },
        "if": { "$dynamicRef": "#meta" },
        "then": { "$dynamicRef": "#meta" },
        "else": { "$dynamicRef": "#meta" },
        "allOf": { "$ref": "#/$defs/schemaArray" },
        "anyOf": { "$ref": "#/$defs/schemaArray" },
        "oneOf": { "$ref": "#/$defs/schemaArray" },
        "not": { "$dynamicRef": "#meta" },
        // content.json
        "contentEncoding": { "type": "string" },
        "contentMediaType": { "type": "string" },
        "contentSchema": { "$dynamicRef": "#meta" },
        // core.json
        "$id": {
            "$ref": "#/$defs/uriReferenceString",
            "$comment": "Non-empty fragments not allowed.",
            "pattern": "^[^#]*#?$"
        },
        "$schema": { "$ref": "#/$defs/uriString" },
        "$ref": { "$ref": "#/$defs/uriReferenceString" },
        "$anchor": { "$ref": "#/$defs/anchorString" },
        "$dynamicRef": { "$ref": "#/$defs/uriReferenceString" },
        "$dynamicAnchor": { "$ref": "#/$defs/anchorString" },
        "$vocabulary": {
            "type": "object",
            "propertyNames": { "$ref": "#/$defs/uriString" },
            "additionalProperties": {
                "type": "boolean"
            }
        },
        "$comment": {
            "type": "string"
        },
        "$defs": {
            "type": "object",
            "additionalProperties": { "$dynamicRef": "#meta" }
        },
        // meta-data.json
        "title": {
            "type": "string"
        },
        "description": {
            "type": "string"
        },
        "default": true,
        "deprecated": {
            "type": "boolean",
            "default": false
        },
        "readOnly": {
            "type": "boolean",
            "default": false
        },
        "writeOnly": {
            "type": "boolean",
            "default": false
        },
        "examples": {
            "type": "array",
            "items": true
        },
        // unevaluated.json
        "unevaluatedItems": { "$dynamicRef": "#meta" },
        "unevaluatedProperties": { "$dynamicRef": "#meta" },
        // format-annotation.json and format-assertion.json
        "format": { "type": "string" },
    },
    "$defs": {
        // validation.json
        "nonNegativeInteger": {
            "type": "integer",
            "minimum": 0
        },
        "nonNegativeIntegerDefault0": {
            "$ref": "#/$defs/nonNegativeInteger",
            "default": 0
        },
        "simpleTypes": {
            "enum": [
                "array",
                "boolean",
                "integer",
                "null",
                "number",
                "object",
                "string"
            ]
        },
        "stringArray": {
            "type": "array",
            "items": { "type": "string" },
            "uniqueItems": true,
            "default": []
        },
        // applicator.json
        "schemaArray": {
            "type": "array",
            "minItems": 1,
            "items": { "$dynamicRef": "#meta" }
        },
        // content.json (empty)
        // core.json
        "anchorString": {
            "type": "string",
            "pattern": "^[A-Za-z_][-A-Za-z0-9._]*$"
        },
        "uriString": {
            "type": "string",
            "format": "uri"
        },
        "uriReferenceString": {
            "type": "string",
            "format": "uri-reference"
        },
        // meta-data.json (empty)
        // unevaluated.json (empty)
        // format-annotation.json (empty)
        // format-assertion.json (empty)
    }
}

The code to perform this validation is as follows:

        : 
        private static Logger log = LoggerFactory.getLogger(some.class);
        :
        String schemaPath = "jsonschema.json";
        String dataPath = "test.schema.json";

        ObjectMapper mapper = new ObjectMapper();

        try {
            String schemaStr = readFile(schemaPath);

            JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909);
            JsonSchema schema = factory.getSchema(schemaStr);

            String dataStr = readFile(dataPath);
            JsonNode dataNode = mapper.readTree(dataStr);

            Set<ValidationMessage> errors = schema.validate(dataNode);
            for (ValidationMessage error : errors) {
                log.error("CLI:certifySchema error message:{}, details:{}, arguments:{}, path:{}, code:{}, type:{}",
                    error.getMessage(), error.getDetails(), error.getArguments(), error.getPath(), error.getCode(), error.getType());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        public String readFile(String path) throws java.io.FileNotFoundException, java.io.IOException {
            String contents = null;
            try(BufferedReader br = new BufferedReader(new FileReader(path))) {
                 StringBuilder sb = new StringBuilder();
                 String line = br.readLine();

                 while (line != null) {
                     sb.append(line);
                     sb.append(System.lineSeparator());
                     line = br.readLine();
                 }
                 contents = sb.toString();
             }
             return contents;
         }

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions