-
Notifications
You must be signed in to change notification settings - Fork 330
Closed
Labels
Description
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;
}