Related
I have looked at many other posts, but can't find a solution to my problem.
I'm trying to validate the following scenario through a json schema:
If isRetired = false then retirementAge, salary and salaryFreq are additionally required.
I have tried the below in addition to a oneOf with 2 schemas to include all the required fields for isRetired = true/false but that never worked either.
Any suggestions would be greatly appreciated.
Thanks in advance!
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": false,
"required": ["input"],
"properties": {
"input": {
"$id": "#root/input",
"title": "Input",
"type": "object",
"required": ["persons"],
"properties": {
"persons": {
"$id": "#root/input/persons",
"title": "Persons",
"type": "array",
"default": [],
"minItems": 1,
"maxItems": 2,
"items": {
"$id": "#root/input/persons/items",
"title": "Items",
"type": "object",
"required": [
"gender",
"age",
"isRetired",
"superConcContPercentage",
"totalSuper",
"riskGrowthPercentage",
"includeAgePension"
],
"properties": {
"firstName": {
"$id": "#root/input/persons/items/firstName",
"title": "Firstname",
"type": "string",
"default": "",
"examples": ["Joe"],
"pattern": "^.*$"
},
"lastName": {
"$id": "#root/input/persons/items/lastName",
"title": "Lastname",
"type": "string",
"default": "",
"examples": ["Bloggs"],
"pattern": "^.*$"
},
"gender": {
"$id": "#root/input/persons/items/gender",
"title": "Gender",
"type": "string",
"default": "",
"examples": ["male"],
"pattern": "^.*$"
},
"age": {
"$id": "#root/input/persons/items/age",
"title": "Age",
"type": "integer",
"examples": [27],
"default": 0,
"minimum": 18,
"maximum": 110
},
"isRetired": {
"$id": "#root/input/persons/items/isRetired",
"title": "Isretired",
"type": "boolean",
"examples": [false],
"default": true
},
"retirementAge": {
"$id": "#root/input/persons/items/retirementAge",
"title": "Retirementage",
"type": "integer",
"examples": [70],
"default": 0,
"minimum": 19,
"maximum": 110
},
"salary": {
"$id": "#root/input/persons/items/salary",
"title": "Salary",
"type": "integer",
"examples": [100000],
"default": 0,
"minimum": 0
},
"salaryFreq": {
"$id": "#root/input/persons/items/salaryFreq",
"title": "Salaryfreq",
"type": "integer",
"examples": [12],
"default": 0
},
"superConcContPercentage": {
"$id": "#root/input/persons/items/superConcContPercentage",
"title": "Superconccontpercentage",
"type": "integer",
"examples": [0],
"default": 0,
"minimum": 0,
"maximum": 100
},
"totalSuper": {
"$id": "#root/input/persons/items/totalSuper",
"title": "Totalsuper",
"type": "integer",
"examples": [10000],
"default": 0,
"minimum": 0
},
"riskGrowthPercentage": {
"$id": "#root/input/persons/items/riskGrowthPercentage",
"title": "Riskgrowthpercentage",
"type": "integer",
"examples": [50],
"default": 0,
"minimum": 0,
"maximum": 100
},
"includeAgePension": {
"$id": "#root/input/persons/items/includeAgePension",
"title": "Includeagepension",
"type": "boolean",
"examples": [false],
"default": true
}
}
},
"if": {
"properties": {
"isRetired": {
"const": false
}
}
},
"then": {
"required": [
"retirementAge",
"salary",
"salaryFreq"
]
},
"else": {
"required": []
}
}
}
}
}
}
I discovered your question while trying to figure this exact use case out for myself. Here is what I came up with:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"trigger": { "type": "boolean" },
"dependentProp": { "type": "string" }
},
"if": {
"properties": {
"trigger": {
"type": "boolean",
"enum": [false]
}
}
},
"then": { "required": ["dependentProp"] }
}
There might be a cleaner way to do it but it worked for me so I'm going with it and moving on haha.
I ended up with the below to satisfy the use case:
"if": {
"properties": {
"isRetired": {
"const": false
}
}
},
"then": {
"properties": {
"salary": {
"$id": "#root/input/persons/items/salary",
"title": "Salary",
"type": "integer",
"examples": [100000],
"default": 0,
"minimum": 0
},
"salaryFreq": {
"$id": "#root/input/persons/items/salaryFreq",
"title": "Salaryfreq",
"type": "integer",
"examples": [12],
"default": 0
}
},
"required": ["salary", "salaryFreq"]
}
I have a JSON like this:
{
"result": [
{
"name": "Adam",
"age": 22
},
{
"name": "John"
},
{
"name": "Justin",
"age": 25
}
]
}
and schema:
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/object1607582431.json",
"title": "Root",
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"$id": "#root/result",
"title": "Result",
"type": "array",
"default": [],
"items": {
"$id": "#root/result/items",
"title": "Items",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"$id": "#root/result/items/name",
"title": "Name",
"type": "string",
"default": "",
"pattern": "^.*$"
},
"age": {
"$id": "#root/result/items/age",
"title": "Age",
"type": "integer",
"default": 0
}
}
}
}
}
}
Here age is an optional property. I am struggling to apply a rule if an optional property is present in one object then it should present in all the objects in that collection. Is there any option available for this?
This is actually a really good use case for the if/then construct that really can't be replicated by using a oneOf like a switch statement. (Well done.)
You're going to want to add the if/then construct to the results property subschema. If the if passes, then the then MUST also apply.
"result": {
...,
"if": {
"contains": { "required": [ "age" ] } // 1
},
"then": {
"items": { "required": [ "age" ] } // 2
}
}
If the the result object contains an item that has an age property,
Require all items to have an age property.
Edit
Full schema:
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/object1607582431.json",
"title": "Root",
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"$id": "#root/result",
"title": "Result",
"type": "array",
"default": [],
"if": { "contains": { "required": [ "age" ] } },
"then": { "items": { "required": [ "age" ] } },
"items": {
"$id": "#root/result/items",
"title": "Items",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"$id": "#root/result/items/name",
"title": "Name",
"type": "string",
"default": "",
"pattern": "^.*$"
},
"age": {
"$id": "#root/result/items/age",
"title": "Age",
"type": "integer",
"default": 0
}
}
}
}
}
}
Instance:
{
"result": [
{
"name": "one"
},
{
"name": "two",
"age": 5
},
{
"name": "three"
}
]
}
Remove the age property or add it to the other items to get the schema to pass.
I'm getting very confused as to why my JSONSchema will not validate my data as expected.
I've appended my JSONShema and Example Date below.
The schema validates the first layer of the nested data structure (the Organisation) without issue, but fails to validate the second layer (User) and below.
Can anybody point me in the right direction as to what I'm doing wrong?
Much appreciated!
JSONSchema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://myorg/json/schemas/report-schedule.json",
"type": "array",
"items": {
"$ref": "#/definitions/Organisation"
},
"definitions": {
"Organisation": {
"type": "object",
"required": [
"organisationId",
"organisationName",
"users"
],
"properties": {
"organisationId": {
"type": "string",
"minLength": 1
},
"organisationName": {
"type": "string",
"minLength": 1
},
"users": {
"type": "array",
"items": {
"ref": "#/definitions/User"
}
}
}
},
"User": {
"type": "object",
"required": [
"name",
"email",
"reports"
],
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"email": {
"type": "string",
"format": "email"
},
"reports": {
"type": "array",
"items": {
"ref": "#/definitions/Report"
}
}
}
},
"Report": {
"type": "object",
"required": [
"reportType",
"reportWeekEndDay",
"sendDay",
"sendHour",
"locations",
"widgets"
],
"properties": {
"reportType": {
"type": "string",
"enum": [
"org-weekly"
]
},
"reportWeekEndDay": {
"type": "integer",
"maximum": 6,
"minimum": 0
},
"sendDay": {
"type": "integer",
"maximum": 6,
"minimum": 0
},
"sendHour": {
"type": "integer",
"maximum": 23,
"minimum": 0
},
"locations": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"widgets": {
"$ref": "#/definitions/Widgets"
}
}
},
"Widgets": {
"type": "array",
"title": "Widgets within a report",
"items": {
"$ref": "#/definitions/Widget"
}
},
"Widget": {
"type": "object",
"required": [
"widgetType",
"metrics",
"comparisonType"
],
"properties": {
"widgetType": {
"type": "string",
"enum": [
"table-individual-locations",
"table-all-locations"
]
},
"comparisonType": {
"type": "string",
"enum": [
"week-on-week",
"3-and-1-month-weekly-avg"
]
},
"metrics": {
"$ref": "#/definitions/Metrics"
}
}
},
"Metrics": {
"type": "array",
"title": "The Available Metrics",
"items": {
"oneOf": [
{
"$ref": "#/definitions/FootFallAndMovement"
},
{
"$ref": "#/definitions/Engagement"
},
{
"$ref": "#/definitions/Occupancy"
},
{
"$ref": "#/definitions/SalesDataTransactions"
},
{
"$ref": "#/definitions/SalesDataConversion"
}
]
}
},
"FootFallAndMovement": {
"type": "object",
"required": [
"title",
"type",
"endpoint",
"queryParams"
],
"properties": {
"title": {
"type": "string",
"minLength": 1
},
"type": {
"type": "string",
"const": "FootFallAndMovement"
},
"endpoint": {
"type": "string",
"minLength": 1
},
"queryParams": {
"type": "object",
"required": [
"excludeStaff"
],
"properties": {
"excludeStaff": {
"type": "boolean"
}
}
}
}
},
"Engagement": {
"type": "object",
"required": [
"title",
"type",
"endpoint",
"queryParams"
],
"properties": {
"title": {
"type": "string",
"minLength": 1
},
"type": {
"type": "string",
"const": "Engagement"
},
"endpoint": {
"type": "string",
"minLength": 1
},
"queryParams": {
"type": "object",
"required": [
"excludeStaff",
"contexts"
],
"properties": {
"additionalFilters": {
"type": "object",
"required": [
"ignoreLingerUnder"
],
"properties": {
"ignoreLingerUnder": {
"type": "integer",
"minimum": 1
}
}
},
"excludeStaff": {
"type": "boolean"
},
"contexts": {
"type": "object",
"required": [
"type",
"value"
],
"properties": {
"type": {
"type": "string",
"const": "taxonomy"
},
"value": {
"type": "string",
"minLength": 1
}
}
}
}
}
}
},
"Occupancy": {
"type": "object",
"required": [
"title",
"type",
"endpoint",
"queryParams"
],
"properties": {
"title": {
"type": "string",
"minLength": 1
},
"type": {
"type": "string",
"const": "Occupancy"
},
"endpoint": {
"type": "string",
"minLength": 1
},
"queryParams": {
"type": "object",
"required": [
"excludeStaff",
"contexts"
],
"properties": {
"excludeStaff": {
"type": "boolean"
},
"contexts": {
"type": "object",
"required": [
"type",
"value"
],
"properties": {
"type": {
"type": "string",
"const": "taxonomy"
},
"value": {
"type": "string",
"minLength": 1
}
}
}
}
}
}
},
"SalesDataTransactions": {
"type": "object",
"required": [
"title",
"type",
"endpoint"
],
"properties": {
"title": {
"type": "string",
"minLength": 1
},
"type": {
"type": "string",
"const": "SalesDataTransactions"
},
"endpoint": {
"type": "string",
"minLength": 1
}
}
},
"SalesDataConversion": {
"type": "object",
"required": [
"title",
"type",
"endpoint",
"queryParams"
],
"properties": {
"title": {
"type": "string",
"minLength": 1
},
"type": {
"type": "string",
"const": "SalesDataConversion"
},
"endpoint": {
"type": "string",
"minLength": 1
},
"queryParams": {
"type": "object",
"required": [
"excludeStaff"
],
"properties": {
"excludeStaff": {
"type": "boolean"
}
}
}
}
}
}
}
Example Data
[
{
"organisationName": "a",
"organisationId": "a",
"users": [
{
"nameWITHERROR": "a"/*e.g. name should be required*/,
"email": "rob#test.com",
"reports": [
{
"reportType": "org-weekly1"/*e.g. this should be an enum*/,
"reportWeekEndDay": 6,
"sendDay": 6,
"sendHour": 6,
"locations": [
"abc",
"bcd"
],
"widgets": [
{
"widgetType": "table-all-locations",
"comparisonType": "3-and-1-month-weekly-avg",
"metrics": [
{
"title": "test",
"type": "FootFallAndMovement1"/*e.g. this enum should fail*/,
"endpoint": "/test",
"queryParams": {
"excludeStaff": true
}
},
{
"title": "test",
"type": "Engagement",
"endpoint": "/test",
"queryParams": {
"excludeStaff": true,
"contexts": {
"type": "taxonomy",
"value": "tests"
}
}
},
{
"title": "test",
"type": "Occupancy",
"endpoint": "/test",
"queryParams": {
"excludeStaff": true,
"contexts": {
"type": "taxonomy",
"value": "tests"
}
}
},
{
"title": "test",
"type": "SalesDataTransactions",
"endpoint": "/test"
},
{
"title": "test",
"type": "SalesDataConversion",
"endpoint": "/test",
"queryParams": {
"excludeStaff": true
}
}
]
}
]
}
]
}
]
}
]
Opening your JSON schema in JSONBuddy shows me immediately that you have used "ref" at /definitions/Organisation/properties/users/items/ref. So there is no validation because the definition is not found.
Using Draft-07
What I got was
valid JSON
What error I expected from audit object was
directory: String length must be greater than or equal to 2
Tried two different validators with same results
https://www.jsonschemavalidator.net/
GoLang https://github.com/xeipuuv/gojsonschema
This is my schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ISAM-Wrapper",
"description": "Validate isam wrapper json",
"type": "object",
"properties": {
"directory": {
"description": "path to location of isam file",
"type": "string",
"minLength": 2
},
"isamFile": {
"description": "isam database file",
"type": "string",
"minLength": 4
},
"isamIndex": {
"description": "isam index file",
"type": "string",
"minLength": 4
},
"port": {
"description": "port number for REST listener",
"type": "integer",
"minimum": 60410,
"maximum": 69999
},
"actions": {
"description": "Which operations are supported",
"type": "object",
"items": {
"properties": {
"create": {
"type": "boolean"
},
"read": {
"type": "boolean"
},
"update": {
"type": "boolean"
},
"delete": {
"type": "boolean"
}
}
},
"required": [
"create",
"read",
"update",
"delete"
]
},
"fields": {
"description": "each object describes one field of the isam file",
"type": "array",
"minItems": 1,
"items": {
"title": "field",
"description": "field schema",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"ordinal": {
"type": "integer",
"minimum": 0
},
"offset": {
"type": "integer",
"minimum": 0
},
"length": {
"type": "integer",
"minimum": 1
},
"dataType": {
"enum": [
"uchar",
"ulong",
"long",
"uint",
"int",
"ushort",
"short"
]
}
},
"required": [
"name",
"ordinal",
"offset",
"length",
"dataType"
]
}
},
"audit": {
"description": "input needed to enable and configure isam auditing",
"type": "object",
"items": {
"properties": {
"enable": {
"enum": [
true,
false
]
},
"directory": {
"type": "string",
"minLength": 2
},
"fileName": {
"type": "string",
"minLength": 4
},
"workDirectory": {
"type": "string",
"minLength": 2
},
"archiveDirectory": {
"type": "string",
"minLength": 2
},
"interval": {
"type": "integer",
"minimum": 1
},
"byteThreshold": {
"type": "integer",
"minimum": 1048576,
"maximum": 1073741824
}
}
},
"required": [
"enable"
],
"if": {
"not": {
"properties": {
"enable": {
"enum": [
false
]
}
}
}
},
"then": {
"required": [
"directory",
"fileName",
"workDirectory",
"archiveDirectory",
"interval",
"byteThreshold"
]
}
}
},
"required": [
"directory",
"isamFile",
"isamIndex",
"port",
"actions",
"fields",
"audit"
]
}
This is my JSON
{
"directory": "./",
"isamFile": "isam.dat",
"isamIndex": "isam.idx",
"port": 60410,
"actions": {
"create": true,
"read": true,
"update": true,
"delete": true
},
"fields": [
{
"name": "F1",
"ordinal": 0,
"offset": 0,
"length": 4,
"dataType": "ulong"
},
{
"name": "F2",
"ordinal": 1,
"offset": 4,
"length": 4,
"dataType": "ulong"
}
],
"audit": {
"enable": true,
"directory": "",
"fileName": "file",
"workDirectory": "./work",
"archiveDirectory": "./archive",
"interval": 5,
"byteThreshold": 1500000
}
}
This issue you have is that your schema is invalid. For both actions and audit you specify these as objects but you don't provide any properties. What you do do, however, is specify an items key (which does nothing here - that's a key on an array) which contains the properties.
Once you correct this error, the schema behaves as you intend, see https://repl.it/repls/BlankWellmadeFrontpage
I'm just starting with Swagger UI and I'm trying to understand how it works.
So far I've entered some JSON (manually) and this is the result:
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "PhakeApps API",
"contact": {
"name": "PhakeApps API team",
"url": "http://phakeapps.com/"
},
"license": {
"name": "Creative Commons 4.0 International",
"url": "http://creativecommons.org/licenses/by/4.0/"
}
},
"host": "api.phakeapps.com",
"basePath": "/v1",
"schemes": [
"http"
],
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"paths": {
"/places/search": {
"post": {
"tags": [
"Places"
],
"description": "Search for (a) place(s) <br /><br /> <b>id</b> - The ID of the request. <br /> <b>api_key</b> - API Key for the platform the request is sent. <i>Currently, not required.</i> <br /> <b>Params</b> - Required. <i>See model & model schema.</i>",
"operationId": "PlacesSearch",
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"parameters": [
{
"name": "request",
"in": "body",
"paramType": "body",
"description": "Object containing the <u>id</u>, <u>api_key</u> and certain <u>params</u>.",
"required": true,
"schema": {
"$ref": "#/definitions/Search"
}
}
],
"responses": {
"200": {
"description": "Success",
"schema": {
"$ref": "#/definitions/PlacesResult"
}
},
"403": {
"description": "Validation error or Server Failure",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
}
}
},
"definitions": {
"PlacesResult": {
"required": [
"data",
"id",
"code"
],
"properties": {
"data": {
"$ref": "#/definitions/Places"
},
"id": {
"type": "integer",
"format": "int32"
},
"code": {
"type": "integer",
"format": "int32"
}
}
},
"Places": {
"required": [
"places"
],
"properties": {
"places": {
"$ref": "#/definitions/Place"
}
}
},
"City": {
"required": [
"id",
"name"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"Neighbourhood": {
"required": [
"id",
"name"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"Cuisine": {
"required": [
"id",
"name"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"Place": {
"required": [
"id",
"name",
"city",
"neighbourhood",
"address",
"cuisine",
"price",
"photos_cnt",
"lat",
"lng",
"is_fav"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"city": {
"type": "array",
"items": {
"$ref": "#/definitions/City"
}
},
"neighbourhood": {
"type": "array",
"items": {
"$ref": "#/definitions/Neighbourhood"
}
},
"address": {
"type": "string"
},
"cuisine": {
"type": "array",
"items": {
"$ref": "#/definitions/Cuisine"
}
},
"price": {
"type": "integer",
"format": "int32"
},
"photos_cnt": {
"type": "integer",
"format": "int32"
},
"lat": {
"type": "double"
},
"lng": {
"type": "double"
},
"is_fav": {
"type": "boolean"
}
}
},
"Search": {
"required": [
"id",
"api_key",
"params"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"api_key": {
"type": "string"
},
"params": {
"$ref": "#/definitions/SearchParams"
}
}
},
"SearchParams": {
"required": [
"user_id",
"city_id",
"people",
"dt",
"locale"
],
"properties": {
"user_id": {
"type": "string",
"default": "956dda4c21c72e48f5f17a7cd783a0f7"
},
"city_id": {
"type": "string",
"default": "4ec4b3e6098c9d23c925b0c2451eb06a"
},
"people": {
"type": "integer",
"format": "int32",
"minimum": 1,
"default": 2
},
"dt": {
"type": "integer",
"format": "int32",
"default": "1427742000"
},
"locale": {
"type": "string",
"default": "bg"
},
"place_id": {
"type": "string",
"default": "0"
},
"neighborhood_id": {
"type": "string",
"default": "0"
},
"cuisine_id": {
"type": "string",
"default": "0"
},
"kids_place": {
"type": "boolean",
"default": false
},
"price": {
"type": "integer",
"format": "int64",
"default": 1
},
"outdoors": {
"type": "boolean",
"default": false
}
}
},
"Error": {
"required": [
"code",
"data"
],
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"data": {
"type": "array",
"items": {
"type": "array"
}
}
}
}
}
}
However, swagger's validator says it's not valid. The error I get is this
[
{
"level": "error",
"domain": "validation",
"keyword": "anyOf",
"message": "instance failed to match at least one required schema among 2",
"schema": {
"loadingURI": "http://json-schema.org/draft-04/schema#",
"pointer": "/properties/type"
},
"instance": {
"pointer": "/definitions/Place/properties/lat/type"
}
}
]
Note that it works as expected (so far). It displays the data (models and models' structure) properly. Make requests and retrieves responses. Yet the validator says it's not valid. (The yellow badge saying 'Invalid', not the red one that says 'Error').
What am I missing?
Your spec is indeed not valid. In your Place definition, you use "type": "double" to describe the type of the lat (and also lng) property, but such a type does not exist.
If you want to describe a numeric value of 'double' size, you should change the definition as follows:
"lng": {
"type": "number",
"format": "double"
}
Do that everywhere you use the double type, and it should resolve that issue.