How to use definitions in JSON schema (draft-04) - json

The rest service response I am working with is similar to following example, I have only included 3 fields here but there are many more:
{
"results": [
{
"type": "Person",
"name": "Mr Bean",
"dateOfBirth": "14 Dec 1981"
},
{
"type": "Company",
"name": "Pi",
"tradingName": "Pi Engineering Limited"
}
]
}
I want to write a JSON schema file for above (draft-04) which will explicitly specify that:
if type == Person then list of required properties is ["type", "name", "dateOfBirth", etc]
OR
if type == "Company" then list of required properties is ["type", "name", "tradingName", etc]
However am unable to find any documentation or example of how to do it.
Currently my JSON schema looks like following:
{
"$schema": "http://json-schema.org/draft-04/schema",
"type": "object",
"required": ["results" ],
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"required": ["type", "name"],
"properties": {
"type": { "type": "string" },
"name": { "type": "string" },
"dateOfBirth": { "type": "string" },
"tradingName": { "type": "string" }
}
}
}
}
}
Any pointers/examples of how I should handle this.

I think the recommended approach is the one shown in Json-Schema web, Example2. You need to use an enum to select schemas "by value". In your case it would be something like:
{
"type": "object",
"required": [ "results" ],
"properties": {
"results": {
"type": "array",
"items": {
"oneOf": [
{ "$ref": "#/definitions/person" },
{ "$ref": "#/definitions/company" }
]
}
}
},
"definitions": {
"person": {
"properties": {
"type": { "enum": [ "person" ] },
"name": {"type": "string" },
"dateOfBirth": {"type":"string"}
},
"required": [ "type", "name", "dateOfBirth" ],
"additionalProperties": false
},
"company": {
"properties": {
"type": { "enum": [ "company" ] },
. . .
}
}
}
}

Sorry,
I don't get the point. The question is about the 'dependencies' keyword which is part of the last JSON Schema specification, right?
I do not find 'dependencies' in the accepted answer (?)
It is briefly explained in the last draft.
But http://usingjsonschema.com explained both property and definition dependencies in the book:
http://usingjsonschema.com/assets/UsingJsonSchema_20140814.pdf
start at page 29 (see, explained at page 30)
"dependencies": {
"shipTo":["shipAddress"],
"loyaltyId":["loyaltyBonus"]
}

Related

JsonSchema - Only allow one item in an array field if another array field is defined

I have the following json and would like to only allow "leads" to contain only a single "id" item if there is a "tokens" array present (with at least one item).
JSON
{
"input": {
"leads": [
{
"id": 795333333760
}
],
"tokens": [
{
"name": "tem_x",
"value": "Renew_all"
},
{
"duration": "90",
"eligibility": "eligible"
}
]
}
I have the following schema, that indicates "tokens" can have more than one item if present and that "leads" is required.
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"input": {
"type": "object",
"properties": {
"leads": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"id": {
"type": "integer"
}
},
"required": [
"id"
]
}
]
},
"tokens": {
"type": "array",
"minItems": 1
}
},
"required": [
"leads",
]
}
},
"required": [
"input"
]
}
Is there a way to ensure that only one item in "leads" can be allowed if "tokens" is present (as it is not defined as a required field). If "tokens" is not defined, then I would like to allow the "leads" array to have more than one item.
I played around with if-then but wasn't able to get it working right. Any help is appreciated.
Thank you.
This is the kind of thing the dependencies keyword is for. It can be done with if/then or implication as well, but dependencies removes all the extra boilerplate needed for those patterns. The following says, if the "tokens" property is defined, then the "leads" property must have at most 1 item. This would go inside your "input" schema.
"dependencies": {
"tokens": {
"properties": {
"leads": { "maxItems": 1 }
}
}
}
Edit: dependencies works in draft-04 thru draft-07. In draft 2019-09 and up, you can use dependentSchemas instead.
try to use like this:
{
"properties": {
"tokens": {},
"leads": {}
},
"anyOf": [{
"required" : ["tokens"]
}, {
"required" : ["leads"]
}]
}
so schema looks like this:
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"input": {
"type": "object",
"properties": {
"leads": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"id": {
"type": "integer"
}
},
"required": [
"id"
]
}
]
},
"tokens": {
"type": "array",
"minItems": 1
}
},
"anyOf": [{
"required" : ["tokens"]
}, {
"required" : ["leads"]
}]
}
},
"required": [
"input"
]
}

Json schema validation for object which may have two forms

I'm trying to figure out how to validate a JSON object which may have 2 forms.
for examples
when there is no data available, the JSON could be
{
"student": {}
}
when there is data available, the JSON could be
{
"student":{
"id":"someid",
"name":"some name",
"age":15
}
}
I wrote the JSON schema in this way, but it seems not working
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://json-schema.org/draft-07/schema#",
"title": "JSON schema validation",
"properties": {
"student": {
"type": "object",
"oneOf": [
{
"required": [],
"properties": {}
},
{
"required": [
"id",
"name",
"age"
],
"properties": {
"id": {
"$id": "#/properties/student/id",
"type": [
"string",
"null"
]
},
"name": {
"$id": "#/properties/student/name",
"type": [
"string",
"null"
]
},
"age": {
"$id": "#/properties/student/age",
"type": [
"number"
]
}
}
}
]
}
}
}
I was wondering is there a way to validate it. Thank you!
An empty properties object, and an empty required object, do nothing.
JSON Schema is constraints based, in that if you don't explicitly constrain what is allowed, then it is allowed by default.
You're close, but not quite. The const keyword can take any value, and is what you want in your first item of allOf.
You can test the following schema here: https://jsonschema.dev/s/Kz1C0
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://example.com/myawesomeschema",
"title": "JSON schema validation",
"properties": {
"student": {
"type": "object",
"oneOf": [
{
"const": {}
},
{
"required": [
"id",
"name",
"age"
],
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"name": {
"type": [
"string",
"null"
]
},
"age": {
"type": [
"number"
]
}
}
}
]
}
}
}

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 if-else condition complex scenario

{
"policyHolder": {
"fullName": "A"
},
"traveller": [
{
"fullName": "B",
"relationship": "Spouse"
},
{
"fullName": "A",
"relationship": "My Self"
}
]
}
In above json, I want to validate that
if "relationship" = "My Self" then fullName must match the fullName in policyHolder
A field relationship must exist in traveller array, else json is invalid
I have tried to create a json schema with if-else, allOf, etc. but nothing works which can do these validations but not able to.
Please help!!
Schema:
{
"type": "object",
"required": [
"policyHolder",
"traveller",
],
"properties": {
"policyHolder": {
"$id": "#/properties/policyHolder",
"type": "object",
"required": [
"fullName"
],
"properties": {
"fullName": {
"$id": "#/properties/policyHolder/properties/fullName",
"type": "string",
}
}
},
"traveller": {
"$id": "#/properties/traveller",
"type": "array",
"minItems": 1,
"items": {
"$id": "#/properties/traveller/items",
"type": "object",
"properties": {
"fullName": {
"$ref": "#/properties/policyHolder/properties/fullName"
},
"relationship": {
"$id": "#/properties/traveller/items/properties/relationship",
"type": "string",
}
},
"required": [
"fullName",
"relationship"
],
}
}
}
}```
It's your first requirement that you're going to have the most trouble with. JSON Schema doesn't support validation of data against data elsewhere in the instance. It's a highly discussed topic, but nothing has been adopted yet. I suggest you verify this with a little code.
For the second, I would suggest you extract some of your subschemas into definitions rather than trying to muck about with IDs. IDs are typically more beneficial if you're referencing them from other documents or if you use short (like single-word) IDs. Defining the ID as its location in the document is redundant; most processors will handle this automatically.
{
"type": "object",
"required": [
"policyHolder",
"traveller",
],
"definitions": {
"person": {
"type": "object"
"properties": {
"fullName": {"type": "string"}
},
"required": ["fullName"]
},
"relationship": { "enum": [ ... ] } // list possible relationships
},
"properties": {
"policyHolder": { "$ref": "#/definitions/person" },
"traveller": {
"type": "array",
"minItems": 1,
"items": {
"allOf": [
{ "$ref": "#/definitions/person" },
{
"properties": {
"relationship": { "$ref": "#/definitions/relationship" }
},
"required": ["relationship"]
}
]
}
}
}
}
(I extracted the relationship into its own enum definition, but this is really optional. You can leave it inline, or even an unrestricted string if you don't have a defined set of relationships.)
This can't currently be done with JSON Schema. All JSON Schema keywords can only operate on one value at a time. There's a proposal for adding a $data keyword that would enable doing this kind of validation, but I don't think it's likely to be adopted. $data would work like $ref except it references the JSON being validated rather than referencing the schema.
Here's what how you would solve your problem with $data.
{
"type": "object",
"properties": {
"policyHolder": {
"type": "object",
"properties": {
"fullName": { "type": "string" }
}
},
"traveler": {
"type": "array",
"items": {
"type": "object",
"properties": {
"fullName": { "type": "string" },
"relationship": { "type": "string" }
},
"if": {
"properties": {
"relationship": { "const": "My Self" }
}
},
"then": {
"properties": {
"fullName": { "const": { "$data": "#/policyHolder/fullName" } }
}
}
}
}
}
}
Without $data, you will have to do this validation in code or change your data structure so that it isn't necessary.

JSON-Schema oneOf for option under root area

I am trying to get the "oneof" to allow for options in root items but can't find an example and what I try gives an error.
I can get it to work if it is under another item but not under the root {'s
Example - a Job Payment that has required fields (jobNum, payee, amount, type, ) and an option for the payment type (checkInfo or dollarAmt). I know this could be done other ways, but I need this method for a more complex schema.
{
"jobNum": "x216",
"payee": "John Doe",
"type": "check",
"amount": "112.25",
"checkInfo": {
"number": "386"
}
}
{
"JobNum": "x216",
"Payee": "John Doe",
"type" : "Cash",
"amount" : "112.25",
"cashInfo" : {
"dollarAmt" : "112",
"coinAmt" : "0.25"
}
}
The following gives me this error - "Unexpected token encountered when reading value for 'oneOf'. Expected StartObject, Boolean, got StartArray"
{
"description": "Job Payment",
"type": "object",
"required": [ "jobNum", "payee", "amount", "type"],
"properties": {
"jobNum": {
"type": "string"
},
"payee": {
"type": "string"
},
"amount": {
"type": "string"
},
"type": {"enum": [ "check", "cash" ]
},
"oneOf": [
{ "$ref": "#/definitions/ptCash" },
{ "$ref": "#/definitions/ptCheck" }
]
},
"definitions": {
"ptCash": {
"properties": {
"checkInfo": {
"number": "string"
}
},
"required": [ "checkInfo" ],
"additionalProperties": false
},
"ptCheck": {
"properties": {
"dollarAmt": {
"type": "string"
},
"coinAmt": {
"type": "string"
}
},
"required": [ "dollarAmt", "coinAmt" ],
"additionalProperties": false
}
},
"additionalProperties": false
}
There are a a few issues with your schema. I fixed it for you below. I won't explain all the changes I made because I think it is mostly pretty clear by reading the schema. If you want more detail on anything, just ask and I'll update the answer with more details.
The oneOf keyword can only appear in a schema. The properties keyword is an object whose values are schemas. When you put "oneOf" directly under properties, it's not interpreted as a keyword, it's interpreted as a property called "oneOf". The validator then complains because the value of property "oneOf" is expected to be a schema, not an array of schemas like the oneOf keyword.
Your use of additionalProperties doesn't work. This keyword doesn't work the way people often assume that it does. JSON Schema keywords are not aware of any state outside of the schema they are in. Let's look at the "ptCheck" branch of your oneOf first. This describes the property "number", says it is required and that there may be no keywords other than "number". Then your top level defines a the properties "jobNum", "payee", "amount", and "type", requires them all and allows no other properties. These two things can never be true at the same time. Even though your schema is valid, there is no JSON value that can ever be valid against this schema. That's why I moved the definitions of "checkInfo" and "cashInfo" to the top level and only put the required part in oneOf. The only downside to this approach is that you can pass both a "checkInfo" and a "cachInfo" object and it will validate. The extraneous property gets ignored. There are ways around this, but they are problematic enough that I don't advise using them.
I always advise people not to use "additionalProperties": false and to ignore unknown properties instead. The reason is that JSON Schema is a constraint system. Any valid JSON is valid against the empty schema ({}) and each keyword in the schema adds some constraint. This is a different approach to what people are used to when defining classes. An empty class describes nothing and valid values are added. We use "additionalProperties": false to get JSON Schema to behave more like defining a class, but trying to get JSON Schema to behave like something it isn't causes challenges like the one you see here.
{
"description": "Job Payment",
"type": "object",
"required": ["jobNum", "payee", "amount", "type"],
"properties": {
"jobNum": { "type": "string" },
"payee": { "type": "string" },
"amount": { "type": "string" },
"type": { "enum": ["check", "cash"] },
"checkInfo": {
"type": "object",
"properties": {
"number": { "type": "string" }
},
"required": ["number"]
},
"cashInfo": {
"type": "object",
"properties": {
"dollarAmt": { "type": "string" },
"coinAmt": { "type": "string" }
},
"required": ["dollarAmt", "coinAmt"]
}
},
"oneOf": [
{ "$ref": "#/definitions/ptCash" },
{ "$ref": "#/definitions/ptCheck" }
],
"definitions": {
"ptCheck": {
"type": "object",
"properties": {
"type": { "enum": ["check"] }
},
"required": ["checkInfo"]
},
"ptCash": {
"type": "object",
"properties": {
"type": { "enum": ["cash"] }
},
"required": ["cashInfo"]
}
},
"additionalProperties": false
}
oneOf should be placed in prope
Have to re-write rule for both ptCash and ptCheck by using type: object
Following schema should work with ptCheck:
{
"description": "Job Payment",
"type": "object",
"required": [ "jobNum", "payee", "amount", "type"],
"properties": {
"jobNum": {
"type": "string"
},
"payee": {
"type": "string"
},
"amount": {
"type": "string"
},
"type": {"enum": [ "check", "cash" ]
}
},
"oneOf": [
{ "$ref": "#/definitions/ptCash" },
{ "$ref": "#/definitions/ptCheck" }
],
"definitions": {
"ptCash": {
"properties": {
"checkInfo": {
"type": "object",
"required": ["number"],
"properties": {
"number": {
"type": "string"
}
}
}
},
"required": [ "checkInfo" ]
},
"ptCheck": {
"properties": {
"cashInfo": {
"type": "object",
"properties": {
"dollarAmt": {
"type": "string"
},
"coinAmt": {
"type": "string"
}
},
"required": ["dollarAmt", "coinAmt"]
}
},
"required": ["cashInfo"]
}
}
}
Provide some example as below:
import jsonschema
import simplejson as json
schema_filename = '47926398.json'
with open(schema_filename, 'r') as f:
schema_data = f.read()
schema = json.loads(schema_data)
# validate with checkInfo
json_obj = {
"jobNum": "x216",
"payee": "John Doe",
"type": "check",
"amount": "112.25",
"checkInfo": {
"number": "386"
}
}
jsonschema.validate(json_obj, schema)
# invalidate
json_obj = {
"jobNum": "x216",
"payee": "John Doe",
"type": "check",
"amount": "112.25",
"checkInfox": {
"number": "386"
}
}
jsonschema.validate(json_obj, schema)
# validate with cashInfo
json_obj = {
"jobNum": "x216",
"payee": "John Doe",
"type": "check",
"amount": "112.25",
"cashInfo": {
"dollarAmt": "400",
"coinAmt": "30"
}
}
jsonschema.validate(json_obj, schema)
# invalidate with cashInfo
json_obj = {
"jobNum": "x216",
"payee": "John Doe",
"type": "check",
"amount": "112.25",
"cashInfox": {
"dollarAmt": "400",
"coinAmt": "30"
}
}
jsonschema.validate(json_obj, schema)
# invalidate with cashInfo.dollarAmtx
json_obj = {
"jobNum": "x216",
"payee": "John Doe",
"type": "check",
"amount": "112.25",
"cashInfo": {
"dollarAmtx": "400",
"coinAmt": "30"
}
}
jsonschema.validate(json_obj, schema)