Json schema for recursive structure not working - json

Here is my json schema. I want a recursive tree-like structure. But the response is passing even when I have invalid objects in the required array.
{
"$schema":"http://json-schema.org/draft-03/schema",
"properties":{
"Result":{
"type":"object",
"properties":{
"Children":{
"$ref":"#/definitions/Node"
}
},
"required":true
}
},
"required":true,
"type":"object",
"definitions":{
"Node":{
"type":"array",
"Items":{
"type":"object",
"properties":{
"Children":{
"$ref":"#/definitions/Node"
}
},
"required":true
}
}
}
}
To check that the JSON Schema validation will validate correctly , I deliberately put an invalid object inside the response -
{"Result":{"title":"title","Children":[{"invalidobject":"invalidobject"}]}}
But it is passing here - https://www.jsonschemavalidator.net/
What I actually want is Children to also have an array of Children and so on. So, only Children having objects with properties - title and Children should be allowed.
Its passing for this response too -
{"Result":{"title":"title","Children":[{"title":45}]}}

Try this schema. Note that this schema will allow children array to be empty even on the first level. After the first level, you have to allow children array to be empty otherwise, the schema will expect infinitely recursive data in order to pass validation.
{
"$schema": "http://json-schema.org/draft-03/schema",
"type": "object",
"properties": {
"Result": {
"$ref": "#/definitions/node",
"required":true,
}
},
"definitions": {
"node": {
"type": "object",
"properties": {
"title": {
"type": "string",
"required": true
},
"Children": {
"type": "array",
"required": true,
"items": {
"$ref": "#/definitions/node"
}
}
},
"additionalProperties": false
}
}
}
In case you do not want to allow children array to be empty at first level try defining the first level separately like this.
{
"$schema": "http://json-schema.org/draft-03/schema",
"type": "object",
"properties": {
"Result": {
"type": "object",
"required": true,
"properties": {
"title": {
"$ref": "#/definitions/title"
},
"Children": {
"minItems": 1,
"$ref": "#/definitions/Children"
}
},
"additionalProperties": false
}
},
"definitions": {
"title": {
"type": "string",
"required": true
},
"Children": {
"type": "array",
"required": true,
"items": {
"$ref": "#/definitions/node"
}
},
"node": {
"type": "object",
"properties": {
"title": {
"$ref": "#/definitions/title"
},
"Children": {
"$ref": "#/definitions/Children"
}
},
"additionalProperties": false
}
}
}
Here the validation results for sample input JSONs validated from https://www.jsonschemavalidator.net/
Input JSON:
{
"Result": {
"title": "title",
"Children": [
{
"invalidobject": "invalidobject"
}
]
}
}
Validation result:
Message:
Property 'invalidobject' has not been defined and the schema does not allow additional properties.
Schema path:
#/definitions/node/additionalProperties
Message:
Required properties are missing from object: title, Children.
Schema path:
#/definitions/node/required
Inpot JSON:
{
"Result": {
"title": "title",
"Children": [
{
"title": 45
}
]
}
}
Validation result:
Message:
Invalid type. Expected String but got Integer.
Schema path:
#/definitions/node/properties/title/type
Message:
Required properties are missing from object: Children.
Schema path:
#/definitions/node/required

Related

Json Schema matching with 'anyOf'

I'd like to be able to manage a json array of 'objects' where each object has a type and properties, and there's a schema error if a mandatory property is missing from an object.
This is my attempt to do this (not including the array part) declaring two object types and saying the object in the json can be either of those types:
{
'definitions':
{
'typeone':
{
'type': 'object',
'properties':
{
'xtype': {'type':'string', 'const':'typeone'},
'num' : {'type':'number'}
},
'required':['xtype', 'num'],
'additionalProperties':false
},
'typetwo':
{
'type': 'object',
'properties':
{
'xtype': {'type':'string', 'const':'typetwo'},
'str' : {'type':'string'}
},
'required':['xtype', 'str'],
'additionalProperties':false
}
},
'anyOf':
[
{ '$ref': '#/definitions/typeone' },
{ '$ref': '#/definitions/typetwo' },
]
}
However, if I feed it json which fails because a mandatory property is missing from an object like this:
{
'xtype': 'typeone'
}
...it errors with JSON does not match any schemas from 'anyOf'. - I can see the reason is that it doesn't know to try to match on the xtype, instead it just considers xtype of 'typeone' invalid and looks to others.
Is there a better way to do anyOf which will hard-match based on one property value (like a 'switch') then give errors about missing other mandatory properties for that object type?
It gets a lot more verbose, but you can use if/then to switch validation based on the "xtype" property.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"allOf": [
{
"if": {
"type": "object",
"properties": {
"xtype": { "const": "typeone" }
},
"required": ["xtype"]
},
"then": { "$ref": "#/definitions/typeone" }
},
{
"if": {
"type": "object",
"properties": {
"xtype": { "const": "typetwo" }
},
"required": ["xtype"]
},
"then": { "$ref": "#/definitions/typetwo" }
}
],
"definitions": {
"typeone": {
"type": "object",
"properties": {
"xtype": {},
"num": { "type": "number" }
},
"required": ["num"],
"additionalProperties": false
},
"typetwo": {
"type": "object",
"properties": {
"xtype": {},
"str": { "type": "string" }
},
"required": ["str"],
"additionalProperties": false
}
}
}
With a small change to the model, you could use dependencies to get a much simpler cleaner schema. Instead of having an "xtytpe" property, you can have a property corresponding to the name of the type. For example, { "typeone": true }.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"dependencies": {
"typeone": {
"type": "object",
"properties": {
"typeone": {},
"num": { "type": "number" }
},
"required": ["num"],
"additionalProperties": false
},
"typetwo": {
"type": "object",
"properties": {
"typetwo": {},
"str": { "type": "string" }
},
"required": ["str"],
"additionalProperties": false
}
}
}

JSON Schema for child objects with different set of keys

I have JSON data of which is an array of data like
[
{
"type": "background_color",
"data": {
"backgroundColor": "F9192D"
}
},
{
"type": "banner_images",
"data": {
"images": [
{
"url": "https://example.com/abc.jpg",
"id": 3085
},
{
"url": "https://example.com/zyx.jpg",
"id": 3086
}
]
}
},
{
"type": "description_box",
"data": {
"text": "Hello 56787"
}
}
]
The data is an array of object which has two keys type and data. The type and keys of the data will be defined by the type of data it has.
Like for background_color type, the data should have backgroundColor property, while for banner_images, data should have images which is an array of other properties.
Till now, What I have done is
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"title": "category schema",
"description": "Used to validate data of category",
"examples": [],
"required": [],
"items": {
"type": "object",
"required": [
"type",
"data"
],
"properties": {
"type": {
"type": "string",
"enum": ["background_color", "banner_images", "description_box"]
},
"data": {
"type": "object" // How to define data property here for each use case
}
}
}
}
I'm not getting how to define the data property for each use case?
You can use if/then/else blocks to define conditional constraints.
The values of if and then are schemas. If the if schema is valid, then the then schema is applied, otherwise, the allOf subschema (allOf[0] in this example) would pass validation.
There are a few different ways to do this, but this is clean when you don't have any additional or special requirements. Please come back if you do =]
In this example, I've added banner_images...
You can test it working here.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"title": "category schema",
"description": "Used to validate data of category",
"items": {
"type": "object",
"required": [
"type",
"data"
],
"properties": {
"type": {
"type": "string",
"enum": [
"background_color",
"banner_images",
"description_box"
]
},
"data": {
"type": "object"
}
},
"allOf": [
{
"if": {
"properties": {
"type": {
"const": "banner_images"
}
}
},
"then": {
"properties": {
"data": {
"required": [
"images"
],
"properties": {
"images": {
"type": "array"
}
}
}
}
}
}
]
}
}
For reference, here's the part of the JSON Schema draft-7 spec document that details the behaviour: https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-6.6

Json schema only validate the first element from an array

I have a json schema, it has an array, I want to validate all items from the array but the schema only validate the first element, why the schema doesn't validate the rest?
Schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"codigoEmpresa": {
"type": "string"
},
"nombreEmpresa": {
"type": "string"
},
"planes": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"codigoPlan": {
"type": "string"
},
"nombrePlan": {
"type": "string"
},
"tipoProducto": {
"type": "integer"
}
},
"required": [
"codigoPlan",
"nombrePlan",
"tipoProducto"
]
}
]
}
},
"required": [
"codigoEmpresa",
"nombreEmpresa",
"planes"
]
}
Invalid json:
{
"codigoEmpresa":"204",
"nombreEmpresa":"Claro",
"planes":[
{
"codigoPlan":"M-PP-Premium-30.03",
"nombrePlan":"Plan Max Premium Libre",
"tipoProducto":1
},
{
"tipoProducto":3
}
]
}
Schema validator:
https://json-schema-validator.herokuapp.com/
The attributes nombrePlan and tipoProducto in second element from the array are required on the json but the schema validator doesn't validate it.
"items": [ { ... } ]
in your schema should be
"items": { ... }
items keyword in this form will apply a single subschema to all elements found in the array.

How to describe this validation requirement in jsonschema?

I want to validate my API json response like this:
{
"code": 0,
"results": [
{"type":1, "abc": 123},
{"type":2, "def": 456}
]
}
I want to validate the objects within results to have a "abc" field when its type is 1, and "def" field when its type is 2. The results may contain arbitrary number of type1 and type2 objects.
Can I specify this in jsonschema? Or must I use a generic validator for elements in results and do validation myself?
You can do this using the anyOf keyword.
An instance validates successfully against this keyword if it validates successfully against at least one schema defined by this keyword's value.
http://json-schema.org/latest/json-schema-validation.html#anchor85
You need to define both types of items and then use anyOf To describe the array items for "results".
{
"type": "object",
"properties": {
"code": { "type": "integer" },
"results": {
"type": "array",
"items": { "$ref": "#/definitions/resultItems" }
}
},
"definitions": {
"resultItems": {
"type": "object",
"anyOf": [
{ "$ref": "#/definitions/type1" },
{ "$ref": "#/definitions/type2" }
]
},
"type1": {
"properties": {
"type": { "enum": [1] },
"abc": { "type": "integer" }
},
"required": ["abc"]
},
"type2": {
"properties": {
"type": { "enum": [2] },
"def": { "type": "integer" }
},
"required": ["def"]
}
}
}

A common JSON Schema for similar structure

I'm completely new to json and json schema, so I have a question (yet I don't know how much it make sense). Can we create a json schema which is common for similar type of structure. For example:
One single schema can be used to validate following json
JSON:
{
"Team_Table":
[{"Name":"New Zealand", "Match":"Six", "Won":"Six"}]
}
And
{
"Story_Taller":
[{"Story":"No Name", "Chapter":"Don't know"}]
}
Similarities:
Both have only one object in the array
Objects have string value.
Dissimilarities:
Number of properties are different
Keys are different in both
Can we do this?
Maybe this helps you along:
{
"properties": {
"Story_Taller": {
"type": "array",
"maxItems": 1,
"items": {
"properties": {
"Chapter": {
"type": "string"
},
"Story": {
"type": "string"
}
},
"additionalProperties": false
}
},
"Team_Table": {
"type": "array",
"maxItems": 1,
"items": {
"properties": {
"Name": {
"type": "string"
},
"Match": {
"type": "string"
},
"Won": {
"type": "string"
}
},
"additionalProperties": false
}
}
},
"oneOf": [
{
"title": "Story_Taller",
"required": [
"Story_Taller"
]
},
{
"title": "Team_Table",
"required": [
"Team_Table"
]
}
]
}
in (short) words:
in your JSON there must be one property of either "Story_Taller" or "Team_Table" with a maximum of 1 item
"oneOf": [ ... ]
Properties of both arrays are defined by items
"Story_Taller" must have "Chapter" and "Story" and no additional properties.
"Team_Table" must have "Name", "Match", "Won" and no additional properties.
And all of them are defined as strings.