"required" keyword in JSON schema - json

I got the below schema from http://json-schema.org/examples.html, i want to know if the required keyword can only come at the top level. or it can also come within the properties if there is a property of type object.I could not find any thing related to this in the specification https://datatracker.ietf.org/doc/html/draft-fge-json-schema-validation-00#section-5.4.3.
{
"title": "Example Schema",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
}
},
"required": ["firstName", "lastName"]
}
So the below example is a valid schema
{
"title":"Example Schema",
"type":"object",
"properties":{
"firstName":{
"type":"string"
},
"lastName":{
"type":"string"
},
"age":{
"type":"object",
"properties":{
"minAge":{
"type":"number"
},
"maxAge":{
"type":"number"
},
"required":[
"minAge",
"maxAge"
]
}
}
},
"required":[
"firstName",
"lastName"
]
}

4.4 Keywords with the possibility to validate container instances (arrays
or objects) only validate the instances themselves and not their
children (array items or object properties).
So I see that yes, you can have those on any level but the validation should be only consider on the same level as required

Yes, required is a valid keyword in any schema. There are no restrictions for nested schemas.
To use your example, the following is a valid schema and will validate the way you want it to.
{
"title": "Example Schema",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"type": "object",
"properties": {
"minAge": {
"type": "number"
},
"maxAge": {
"type": "number"
}
},
"required": [
"minAge",
"maxAge"
]
}
},
"required": [
"firstName",
"lastName"
]
}

The required keyword can be present in any schema. This is true of all schema keywords.
(There is a special-case for the meta-keyword $schema, for which it is advisable to only have in the top level)

Related

JsonSchema definition path and subschema re-use

Lets say I have two schemas defined as follows -
ADDRESS_CLASS_SCHEMA_DEFINITION = {
"title": "Address",
"type": "object",
"properties": {
"country_code": {
"$ref": "#/definitions/CountryCode"
},
"city_code": {
"title": "City Code",
"type": "string"
},
"zipcode": {
"title": "Zipcode",
"type": "string"
},
"address_str": {
"title": "Address Str",
"type": "string"
}
},
"required": [
"country_code",
"city_code",
"zipcode"
],
"definitions": {
"CountryCode": {
"title": "CountryCode",
"description": "An enumeration.",
"enum": [
"CA",
"USA",
"UK"
],
"type": "string"
}
}
}
EMPLOYEE_CLASS_SCHEMA_DEFINITION = {
"title": "Employee",
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "integer"
},
"name": {
"title": "Name",
"type": "string"
},
"email": {
"title": "Email",
"type": "string"
},
"telephone": {
"title": "Telephone",
"type": "string"
},
"address": {
"$ref": "#/definitions/Address"
}
},
"required": [
"id",
"name",
"email"
],
"definitions": {
"Address": ADDRESS_CLASS_SCHEMA_DEFINITION
}
}
I'm trying to re-use sub-schema definitions by defining a constant and referencing them individually in definitions (for example address-schema is referenced through constant in employee-schema definition). This approach works for individual schemas, however there seems to be a json-pointer path issue for Employee schema - #/definitions/CountryCode wouldn't resolve in Employee schema. I was assuming that #/definitions/CountryCode would be a relative path on Address schema as its scope is defined on a sub-schema, but my understanding seems wrong. I can make it work by flattening out like below, however I donot want to take this route -
{
"title": "Employee",
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "integer"
},
"name": {
"title": "Name",
"type": "string"
},
"email": {
"title": "Email",
"type": "string"
},
"telephone": {
"title": "Telephone",
"type": "string"
},
"address": {
"$ref": "#/definitions/Address"
}
},
"required": [
"id",
"name",
"email"
],
"definitions": {
"CountryCode": {
"title": "CountryCode",
"description": "An enumeration.",
"enum": [
"CA",
"USA",
"UK"
],
"type": "string"
},
"Address": {
"title": "Address",
"type": "object",
"properties": {
"country_code": {
"$ref": "#/definitions/CountryCode"
},
"city_code": {
"title": "City Code",
"type": "string"
},
"zipcode": {
"title": "Zipcode",
"type": "string"
},
"address_str": {
"title": "Address Str",
"type": "string"
}
},
"required": [
"country_code",
"city_code",
"zipcode"
]
}
}
}
I'm wondering how to fix this, I've briefly looked into jsonschema-bundling and using $id but from best practices it seems like the general recommendation is to use $id when dealing with URI's alone. Would like to know about best practices and how to fix this problem, would also appreciate if someone can point me on how to use $id correctly (for example, constant based approach seems to work when I provide identifiers like $id: Address, $id: Employee). Thanks in advance.
JSON Schema implementations work in JSON land. When you combine your schemas in your example above, presumably in javascript/node.js, by the time it gets to the JSON Schema implementation for validation execution, any knowledge that there were separate schemas is lost. (It's generally not considered that this approach is the best approach.)
The EASY fix here SHOULD be just to define $id in each of the roots of your schemas. These should be a fully qualfied URI. It doesn't really matter what they are at this point. They could be https://example.com/a and https://example.com/b. Then, in the primary schema, you can do $ref: https://example.com/b.
Implementations should provide you with a way to load in your other/non-primary schemas so the $id values can be stored in an index. Using $id in your other schema with a fully qualified URI will signify a "resource boundary".
https://json-schema.hyperjump.io is the only web playground to support multiple files/schemas/"Schema Resources", so you can test this out there to confirm your expectations.
Not all implementations make it easy or even provide a means to import your other schemas, but they should.
If you have follow up questions, feel free to leave a comment, or join the JSON Schema slack server if it would be off-topic for StackOverflow.

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)

JSON schema "oneOf" for validating existence of keys

I'm trying to create a JSON schema that validates against multiple different entities that have mostly the same attributes but only differ in a few attributes.
{
"firstname": "Bat",
"lastname": "man",
"email": "batman#gmail.com"
}
{
"firstname": "Super",
"lastname": "man",
"phone": "543-453-4523"
}
{
"firstname": "Wonderwo",
"lastname": "man",
"email": "wonderwoman#gmail.com"
}
Basically I want to create a single schema that makes sure the last name is "man" and either have a phone or email attribute.
I was trying to implement this using oneOf, like this:
{
"properties": {
"firstname": {
"type": "string"
},
"lastname": {
"type": "string",
"pattern": "man"
},
"oneOf": [{
"email": {
"type": "string"
},
"phone": {
"type": "string"
}
}]
}
}
But I don't think this works. Is something like this even possible with JSON schema? And how can I achieve this?
You have several problems:
"oneOf" is a keyword, in cannot be used inside properties.
items inside "oneOf" should be schemas, what you have there is not.
"anyOf" is almost always better than "oneOf", unless you really need an exclusive "OR"
"pattern" is a wrong keyword here, you need "enum" (or draft-06 "const")
You need:
{
"type": "object",
"required": ["firstname", "lastname"],
"properties": {
"firstname": {
"type": "string"
},
"lastname": {
"type": "string",
"enum": ["man"]
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
}
},
"anyOf": [
{ "required": ["email"] },
{ "required": ["phone"] }
]
}

Can maxItems/minItems be used with a $ref in a JSON schema

Given a JSON schema with the following in the definitions section:
"phoneNumber": {
"type": "object",
"properties": {
"countryCode": {
"type": "number"
},
"areaCode": {
"type": "number"
},
"number": {
"type": "number"
},
"extension": {
"type": "number"
},
"service": {
"type": "string",
"enum": ["Voice", "Fax", "Data"]
},
"class": {
"type": "string",
"enum": ["Switchboard", "Direct", "PA", "Mobile"]
}
}
}
If I want to include phoneNumber elsewhere using a $ref and want the JSON to validate if it contains multiple occurrences of phoneNumber, can I use maxItems/minItems:
"person": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"phoneNumber": {
"$ref": "#/definitions/phoneNumber"
//can I use maxItems/minItems here?
}
}
}
Can I use maxItems and minItems here, or would I have to do something like this below for it to validate:
"phoneNumber": {
"allOf": { "$ref": "#/definitions/phoneNumber" },
"maxItems": 4
}
$ref must stand alone. The option you identified using allOf is the best way to do it.
Any members other than "$ref" in a JSON Reference object SHALL be ignored.
https://datatracker.ietf.org/doc/html/draft-pbryan-zyp-json-ref-03#section-3

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

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"]
}