JSONSchema v4 - Error when resolving schema reference - Definitions and References - jsonschema

When trying to validate the following schema using http://www.jsonschemavalidator.net/,
{
"id": "http://some.site.somewhere/entry-schema#",
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "schema for the FormularSpecification",
"definitions": {
"elementId": {
"id": "http://jsonschema.net/elementId",
"type": "string"
},
"mappingKey": {
"id": "http://jsonschema.net/mappingKey",
"type": "string"
},
"elementType": {
"id": "http://jsonschema.net/elementType",
"type": "string"
},
"length": {
"id": "http://jsonschema.net/length",
"type": "integer"
},
"label": {
"id": "http://jsonschema.net/label",
"type": "string"
},
"content": {
"id": "http://jsonschema.net/content",
"type": "string"
},
"placeholder": {
"id": "http://jsonschema.net/placeholder",
"type": "string"
},
"date": {
"id": "http://jsonschema.net/date",
"type": "string"
},
"option": {
"id": "http://jsonschema.net/option",
"type": "object",
"properties": {
"elementId": { "$ref": "#/definitions/elementId" },
"label": { "$ref": "#/definitions/label" }
},
"required": ["elementId", "label"]
},
"options": {
"id": "http://jsonschema.net/options",
"type": "array",
"items": { "$ref": "#/definitions/option" },
"minItems": 1,
"uniqueItems": true
},
"textfield": {
"id": "http://jsonschema.net/textfield",
"type": "object",
"properties": {
"elementId": { "$ref": "#/definitions/elementId" },
"length": { "$ref": "#/definitions/length" },
"label": { "$ref": "#/definitions/label" },
"placeholder": { "$ref": "#/definitions/placeholder" },
"textfieldType": {
"enum": [ "text", "ext4", "btrfs" ]
}
},
"required": ["elementId", "length", "label", "placeholder", "textfieldType"]
},
"checkbox": {
"id": "http://jsonschema.net/checkbox",
"type": "object",
"properties": {
"label": { "$ref": "#/definitions/label" }
},
"required": ["label"]
},
"radio": {
"id": "http://jsonschema.net/radio",
"type": "object",
"properties": {
"label": { "$ref": "#/definitions/label" },
"options": { "$ref": "#/definitions/options" }
},
"required": ["label", "options"]
},
"dropdown": {
"id": "http://jsonschema.net/dropdown",
"type": "object",
"properties": {
"label": { "$ref": "#/definitions/label" },
"options": { "$ref": "#/definitions/options" }
},
"required": ["label", "options"]
},
"validator": {
"id": "http://jsonschema.net/validator",
"type": "object",
"properties": {
"elementId": { "$ref": "#/definitions/elementId" }
}
},
"validators": {
"id": "http://jsonschema.net/validators",
"type": "array",
"items": { "$ref": "#/definitions/validator" }
},
"interactiveDetails": {
"type": "object",
"oneOf": [
{ "textfield": { "$ref": "#/definitions/textfield" } },
{ "checkbox": { "$ref": "#/definitions/checkbox" } },
{ "radio": { "$ref": "#/definitions/radio" } },
{ "dropdown": { "$ref": "#/definitions/dropdown" } },
{ "date": { "$ref": "#/definitions/date" } }
]
},
"interactive": {
"id": "http://jsonschema.net/interactive",
"type": "object",
"properties": {
"elementId": { "$ref": "#/definitions/elementId" },
"elementType": { "$ref": "#/definitions/elementType" },
"mappingKey": { "$ref": "#/definitions/mappingKey" },
"validators": { "$ref": "#/definitions/validators" },
"interactiveDetails" : { "$ref": "#/definitions/interactiveDetails" }
},
"required": ["elementId", "elementType", "mappingKey", "validators"]
},
"interactives": {
"id": "http://jsonschema.net/interactives",
"type": "array",
"items": { "$ref": "#/definitions/interactive" }
},
"description": {
"id": "http://jsonschema.net/description",
"type": "object",
"properties": {
"elementId": { "$ref": "#/definitions/elementId" },
"elementType": { "$ref": "#/definitions/elementType" },
"content": { "$ref": "#/definitions/content" }
},
"required": ["elementId", "elementType", "content"]
},
"descriptions": {
"items": { "$ref": "#/definitions/description" }
},
"children": {
"items": {
"anyOf": [
{ "$ref": "#/definitions/group" },
{ "$ref": "#/definitions/question" }
]
},
"minItems": 1
},
"question": {
"type": "object",
"properties": {
"elementId": { "$ref": "#/definitions/elementId" },
"descriptions": { "$ref": "#/definitions/descriptions" },
"interactives": { "$ref": "#/definitions/interactives" }
},
"required": ["elementId", "descriptions", "interactives"]
},
"group": {
"type": "object",
"properties": {
"elementId": { "$ref": "#/definitions/elementId" },
"descriptions": { "$ref": "#/definitions/descriptions" },
"children": { "$ref": "#/definitions/children"}
},
"required": ["elementId", "descriptions", "children"]
}
},
"type": "object",
"properties": {
"elementId": { "$ref": "#/definitions/elementId" },
"description": { "$ref": "#/definitions/descriptions" },
"children": { "$ref": "#/definitions/children" }
},
"required": [
"elementId",
"descriptions",
"children"
]
}
I'm getting the following error:
Error when resolving schema reference '#/definitions/elementId'. Path 'definitions.description.properties.elementId', line 135, position 30.
I cannot figure out what the problem is. I scanned the documentation several times and had a look at tutorials, but I do not have any clue.

The semantics of the id keyword are are a bit confusing. I'm not sure I completely understand it myself. In general it is almost never a good idea to include id anywhere other than the root of your schema.
The "id" keyword (or "id", for short) is used to alter the resolution scope. When an id is encountered, an implementation MUST resolve this id against the most immediate parent scope. The resolved URI will be the new resolution scope for this subschema and all its children, until another id is encountered.
http://json-schema.org/latest/json-schema-core.html#anchor27
Consider the following excerpt from you schema. Because you include the id keyword, your "elementId" and "label" $refs don't resolve against the root of the document as you expect, they resolve from the nearest parent schema id.
"option": {
"id": "http://jsonschema.net/option",
"type": "object",
"properties": {
"elementId": { "$ref": "#/definitions/elementId" },
"label": { "$ref": "#/definitions/label" }
},
"required": ["elementId", "label"],
"definitions": { ... } <-- your $refs expect values here
}
},
I have seen that in some circles, people write schemas with ids for every subschema. I'm not sure what benefit they think they are getting out of doing that, but I suspect that they think of id as just a label and don't understand how it alters resolution scope.
If you do have good reason to use ids everywhere and want to leave them in, you can always explicitly reference the root id when you have a conflict.
{ "$ref": "http://some.site.somewhere/entry-schema#definitions/elementId" }

Related

Easy way to group custom defined types to use for reference?

How to reference types by group instead of doing it individually?
For example here, I want "sum" and "subtract" types to accept types that are "number"-related types only("number", "sum", "subtract"). While "concatenate" can accept both "number" and "string"-related types. Including their selves. This will be really helpful for me if in case I added more types and want them to be automatically included.
"sum": {
"title": "sum",
"type": "object",
"properties": {
"values": {
"type": "array",
"items": {
"anyOf": [
{
"type": "number"
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
},
"required": [
"values"
]
},
"subtract": {
"title": "subtract",
"type": "object",
"properties": {
"minuend": {
"type": "number"
},
"subtrahend": {
"type": "number"
}
},
"required": [
"minuend",
"subtrahend"
]
},
"concatenate": {
"title": "concatenate",
"type": "object",
"properties": {
"strings": {
"type": "array",
"items": {
"anyOf": [
{
"type": "number"
},
{
"type": "string"
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
},
"required": [
"strings"
]
}
}
Here's the full schema:
{
"definitions": {
"sum": {
"title": "sum",
"type": "object",
"properties": {
"values": {
"type": "array",
"items": {
"anyOf": [
{
"type": "number"
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
},
"required": [
"values"
]
},
"subtract": {
"title": "subtract",
"type": "object",
"properties": {
"minuend": {
"type": "number"
},
"subtrahend": {
"type": "number"
}
},
"required": [
"minuend",
"subtrahend"
]
},
"concatenate": {
"title": "concatenate",
"type": "object",
"properties": {
"strings": {
"type": "array",
"items": {
"anyOf": [
{
"type": "number"
},
{
"type": "string"
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
},
"required": [
"strings"
]
}
},
"title": "Step",
"type": "object",
"properties": {
"value": {
"oneOf": [
{
"type": "number",
"minimum": 0
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
}

Content of an property dependent on another property value

I have the following schema, which "works", but does not enforce all the rules required of it.
I get JSON with a series of questions that have a templateType and data properties. There are different templates for each type, and the type must fit the template (or the client doesn't know how to layout the data).
The schema validates the templateType as an enum, and that the data fits one of the templates, but there's no correlation between the type and data structure (e.g. I could get templateType yesNo and data structure for multiSelect).
I'd like it to validate that the templateType matches the data structure. I can't change the format of the generated JSON, only the schema that validates it. None of the questions I've looked at seem to provide a solution.
For help, the schema can be pasted into the editor at http://jeremydorn.com/json-editor/, which generates a form from the schema and JSON data based on selections and data entered into the form.
{
"definitions": {
"question": {
"type": "array",
"title": "Question",
"items": {
"$ref": "#/definitions/template"
}
},
"template": {
"type": "object",
"title": "Question template",
"required": ["templateType","data"],
"properties": {
"templateType": {
"type": "string",
"enum": ["yesNo","multiSelect"]
},
"data": {
"oneOf": [
{"$ref": "#/definitions/yesNo"},
{"$ref": "#/definitions/multiSelect"}
]
}
}
},
"yesNo": {
"type": "object",
"title": "Yes/No question",
"additionalProperties": false,
"properties": {
"label": {
"type": "string"
}
}
},
"multiSelect": {
"type": "array",
"title": "Multi-select question",
"items": {
"type": "string",
"title": "Label for option",
"additionalProperties": false
}
}
},
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"items": {
"$ref": "#/definitions/question"
}
}
Have you considered using if, then, else keywords in your schema? They're part of JSON Schema draft-07
It would look like this:
{
"definitions": {
"question": {
"type": "array",
"title": "Question",
"items": {
"$ref": "#/definitions/template"
}
},
"template": {
"type": "object",
"title": "Question template",
"required": ["templateType","data"],
"properties": {
"templateType": {
"type": "string",
"enum": ["yesNo","multiSelect"]
},
"data": {
"if": { "properties": { "templateType": { "pattern": "^yesNo$" } } },
"then": { "$ref": "#/definitions/yesNo" },
"else": { "$ref": "#/definitions/multiSelect" }
}
}
},
"yesNo": {
"type": "object",
"title": "Yes/No question",
"additionalProperties": false,
"properties": {
"label": {
"type": "string"
}
}
},
"multiSelect": {
"type": "array",
"title": "Multi-select question",
"items": {
"type": "string",
"title": "Label for option",
"additionalProperties": false
}
}
},
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"items": {
"$ref": "#/definitions/question"
}
}
If if/then/else isn't supported by your validator, an alternative could be:
{
"definitions": {
"question": {
"type": "array",
"title": "Question",
"items": {
"$ref": "#/definitions/template"
}
},
"template": {
"type": "object",
"title": "Question template",
"required": ["templateType","data"],
"anyOf": [
{
"properties": {
"templateType": { "type": "string", "pattern": "yesNo" },
"data": { "$ref": "#/definitions/yesNo" }
}
},
{
"properties": {
"templateType": { "type": "string", "pattern": "multiSelect" },
"data": { "$ref": "#/definitions/multiSelect" }
}
}
]
},
"yesNo": {
"type": "object",
"title": "Yes/No question",
"additionalProperties": false,
"properties": {
"label": {
"type": "string"
}
}
},
"multiSelect": {
"type": "array",
"title": "Multi-select question",
"items": {
"type": "string",
"title": "Label for option",
"additionalProperties": false
}
}
},
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"items": {
"$ref": "#/definitions/question"
}
}

JSONSchema validation failure with $ref (Draft v3)

I have created a JSON schema following the draft v3 specifications. Schema looks like this:
{
"$schema": "http://json-schema.org/draft-03/schema#",
"additionalProperties": false,
"type": "object",
"properties": {
"ExecutionPlanList": {
"type": "array",
"items": [{
"type": "object",
"properties": {
"model": {
"required": true,
"properties": {
"featureList": {
"required": true,
"items": {
"properties": {
"featureName": {
"type": ["string", "null"]
},
"featureType": {
"type": "string"
}
},
"type": "object"
},
"type": "array"
},
"modelId": {
"required": true,
"type": "string"
}
},
"type": "object"
},
"cascadeSteps": {
"required": false,
"items": {
"properties": {
"binaryModel": {
"$ref": "#/properties/ExecutionPlanList/items/properties/model",
"required": true
},
"threshold": {
"required": true,
"default": "0.0",
"maximum": 100.0,
"type": "number"
},
"backupModel": {
"$ref": "#/properties/ExecutionPlanList/items/properties/model",
"required": true
}
}
},
"type": "array"
},
"marketplaceId": {
"required": true,
"type": "integer"
}
}
}]
}
},
"required": true
}
Essentially, executionPlanList contains list of model and cascadeStep, and each cascadeStep contains two models with a number. So I'm trying to re-use the schema for model in cascadeStep, but validation (https://www.jsonschemavalidator.net/) is failing with Could not resolve schema reference '#/properties/ExecutionPlanList/items/properties/model'.
Would appreciate any pointers on what's wrong with this schema.
what about adding a 'definitions' and refer like this:
{
"$schema": "http://json-schema.org/draft-03/schema#",
"additionalProperties": false,
"type": "object",
"definitions": {
"model": {
"required": true,
"properties": {
"featureList": {
"required": true,
"items": {
"properties": {
"featureName": {
"type": ["string", "null"]
},
"featureType": {
"type": "string"
}
},
"type": "object"
},
"type": "array"
},
"modelId": {
"required": true,
"type": "string"
}
},
"type": "object"
}
},
"properties": {
"ExecutionPlanList": {
"type": "array",
"items": [{
"type": "object",
"properties": {
"model": {
"$ref" : "#/definitions/model"
},
"cascadeSteps": {
"required": false,
"items": {
"properties": {
"binaryModel": {
"$ref" : "#/definitions/model",
"required": true
},
"threshold": {
"required": true,
"default": "0.0",
"maximum": 100.0,
"type": "number"
},
"backupModel": {
"$ref" : "#/definitions/model",
"required": true
}
}
},
"type": "array"
},
"marketplaceId": {
"required": true,
"type": "integer"
}
}
}]
}
},
"required": true
}
It should be '#/properties/ExecutionPlanList/items/0/properties/model'
The schema validates only the first item, by the way.

TypeError: datastore.getRootCollection is not a function

I assumed a dstore that was created using dmodel was still a dstore and inherited all the dstore functionality. So there's a method listed called getRootCollection but when i try to run this method on the store it fails with an error (no such function)
Here is my code
<script>
require(
[
'dojo/_base/declare',
'dstore/Memory',
'dmodel/extensions/jsonSchema',
'dmodel/validators/StringValidator',
'dmodel/store/Validating',
"dmodel/Model"
],
function (declare, Memory, jsonSchema, StringValidator, Validating, Model) {
var vMem = (declare([Memory, Validating]))({
Model: jsonSchema(
{
"$schema": "http://json-schema.org/draft-04/schema",
"description": "my schema",
"type": "object",
"properties": {
"page": {
"type": "object",
"properties": {
"detailsCanvas": {
"description": "test value",
"type": "object",
"$ref": "#/definitions/details"
}
}
},
"elements": {
"type": "array",
"items": {
"title": "Element",
"type": "object",
"properties": {
"id": {
"type": "string"
},
"positionX": {
"description": "The X coordinate",
"type": "number"
},
"elementSpecificProperties": {
"type": "object",
"oneOf": [
{ "$ref": "#/definitions/label" }
]
}
}
}
}
},
"definitions": {
"details": {
"type": "object",
"properties": {
"height": {
"type": "string"
}
}
},
"label": {
"type": "object",
"properties": {
"value": {
"type": "string"
}
}
}
}
}
)
});
vMem.setData(
{
"page": {
"detailsCanvas": {
"height": "100px"
}
},
"elements": [
{
"id": "1",
"positionX": 20,
"elementSpecificProperties": {
"value": "value_1"
}
},
{
"id": "2",
"positionX": 5,
"elementSpecificProperties": {
"value": "value_2"
}
}
]
});
var blah = vMem.getRootCollection(); //type error - getRootCollection is not a function
});
</script>
getRootCollection() is part of the Tree model of dstore. You only mixin the dstore/Memory. So that is why you get that error. Checkout the documentation of dstore for more information.

JSON schema for object with either A and B or C required properties

I have two possible JSON objects for one request:
{
"from": "string",
"to": "string",
"text": "string"
}
or
{
"number": "integer",
"text": "string"
}
In both cases "text" property is optional. Other properties are required (either "number, or both "from" and "to").
What will be the correct JSON schema to validate this?
Here is another solution that I think is a bit more clear. The dependencies clause ensures that "from" and "to" always come as a pair. Then the oneOf clause can be really simple and avoid the not-required boilerplate.
{
"type": "object",
"properties": {
"from": { "type": "string" },
"to": { "type": "string" },
"number": { "type": "integer" },
"text": { "type": "string" }
},
"dependencies": {
"from": ["to"],
"to": ["from"]
},
"oneOf": [
{ "required": ["from"] },
{ "required": ["number"] }
]
}
Finally managed to build the correct scheme.
{
"definitions": {
"interval": {
"type": "object",
"properties": {
"from": {
"type": "string"
},
"to": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": ["from", "to"],
"not": {
"required": ["number"]
}
},
"top": {
"type": "object",
"properties": {
"number": {
"type": "integer"
},
"text": {
"type": "string"
}
},
"required": ["number"],
"allOf": [
{
"not": {
"required": ["from"]
}
},
{
"not": {
"required": ["to"]
}
}
]
}
},
"type": "object",
"oneOf": [
{"$ref": "#/definitions/interval"},
{"$ref": "#/definitions/top"}
]
}