I have this schema:
{
"$schema": "http://json-schema.org/draft-07/schema",
"definitions": {
"person": {
"properties": {
"name": { "type": "string" }
}
},
"employee": {
"$ref": "#/definitions/person",
"properties": {
"salary": { "type": "number" }
}
}
},
"properties": {
"Entry": {
"$ref": "#/definitions/employee"
},
},
}
And surprisingly, the following JSON is valid under the schema:
{
"Entry": {
"name": "John",
"salary": "1234"
}
}
Can anyone explain how $ref works here? Why is this JSON valid?
-- EDIT --
I found that if I change
"$schema": "http://json-schema.org/draft-07/schema"
to
"$schema": "http://json-schema.org/draft-05/schema"
it will work as expected, but only "draft-05" works; others like "draft-04" "draft-06" are not working.
In JSON Schema versions up to and including draft 7, when the $ref keyword is adjacent to any other keyword, the other keywords are ignored.
You can work around this by enclosing the $ref in an allOf:
...
"employee": {
"allOf": [ { "$ref": "#/definitions/person" } ],
"properties": {
"salary": { "type": "number" }
}
}
},
...
Related
In JSON Schema, I can use require to ensure that a property exists on the same level of the hierarchy, but I'm having trouble validating for nested ones.
Suppose I have following JSON Schema:
{
"type": "object",
"properties": {
"my_type": {
"type": "string"
},
"t1_data": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
},
"t2_data": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
}
}
}
How would I specify the following validations?
if my_type == "type1", then t1_data.id must exist
if my_type == "type2", then t2_data.id must exist
if my_type is anything else, validation passes
I've tried using the require and anyOf constructs but I could only get them to work at the same level of the hierarchy.
Thanks,
A possible solution is to combine allOf and if-then. It is a little bit verbose but I am not aware of any shorter way. Here is the schema for the case "type1":
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"description": "JSON schema generated with JSONBuddy https://www.json-buddy.com",
"type": "object",
"properties": {
"my_type": {
"type": "string"
},
"t1_data": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
},
"t2_data": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
}
},
"allOf": [
{
"if": {
"properties": {
"my_type": {
"const": "type1"
}
}
},
"then": {
"properties": {
"t1_data": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
},
"required": [ "id" ]
}
}
}
}
]
}
"type2" would be quite the same as next schema in the allOf array.
I want to reference each property of the json object in a different file
This is what I'd like to do (but the validations are not working for me):
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties":
{
"id": { "$ref": "type/common.json#def_id" },
"title": { "$ref": "type/book.json#def_title" }
}
}
...
# type/common.json
{
"def_id":{
"id": {
"type": "string",
"format": "uuid",
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
}
}
}
# type/book.json
{
"def_title":{
"title": {
"anyOf": [
{
"type": "null"
},
{
"type": "string"
}
]
}
}
}
If you could help me I would be very grateful
I was referencing it wrong, it's work
{
"id": { "$ref": "type/common.json#/def_id/id" },
"title": { "$ref": "type/book.json#/def_title/title" }
}
this works for me
I've got a Pet object that could be either a dog or a cat
Depending on what noise they make I'd like to then be able to validate other fields.
schema:
{
"$id": "http://example.com",
"definitions": {
"pet": {
"type": "object",
"properties": {
"noise": {
"enum": [
"bark",
"meow"
]
}
}
},
"dog": {
"$ref": "#/definitions/pet",
"properties": {
"noise": {
"const": "bark"
},
"tail": {
"enum": [
"short",
"long"
]
}
}
},
"cat": {
"$ref": "#/definitions/pet",
"properties": {
"noise": {
"const": "meow"
},
"tail": {
"enum": [
"wavy",
"slinky"
]
}
}
}
},
"type": "object",
"properties": {
"pets": {
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/dog",
"$ref": "#/definitions/cat"
}
]
}
}
}
}
This works when running the following json through:
{"pets":[{"noise":"meow","tail":"wavy"}]}
but not when running:
{"pets":[{"noise":"bark","tail":"long"}]}
[$.pets[0].tail: does not have a value in the enumeration [wavy, slinky], $.pets[0].noise: must be a constant value meow]
or
{"pets":[{"noise":"bark","tail":"long"},{"noise":"meow","tail":"wavy"}]}
[$.pets[0].tail: does not have a value in the enumeration [wavy, slinky], $.pets[0].noise: must be a constant value meow]
I can get this working by using if/else in the json schema, but requires another type to avoid a circular dependency:
"petWithConstraints": {
"$ref":"#/definitions/pet",
"allOf": [
{
"if": {
"properties": {
"noise": {
"const": "bark"
}
}
},
"then": {
"$ref": "#/definitions/dog"
}
},
{
"if": {
"properties": {
"noise": {
"const": "meow"
}
}
},
"then": {
"$ref": "#/definitions/cat"
}
}
]
}
}
This means for every new definition it also requires another if statement.
Is there a better method of doing this? (without the extra definition/if statement)
For those that come across this, this was a syntactical error.
Each ref should have been in it's own code block.
The corrected part of the schema looks like the following:
"properties": {
"pets": {
"type": "array",
"items": {
"anyOf": [
{ // Notice each $ref is encapsulated in it's own block
"$ref": "#/definitions/cat"
},
{
"$ref": "#/definitions/dog"
}
]
}
}
}
Running the following json through gave expected results
{"pets":[{"noise":"bark","tail":"long"},{"noise":"meow","tail":"wavy"}]}
[]
{"pets":[{"noise":"bark","tail":"long"},{"noise":"meow","tail":"wavy"},{"noise":"meow","tail":"slinky"},{"noise":"bark","tail":"short"}]}
[]
{"pets":[{"noise":"bark","tail":"long"},{"noise":"meow","tail":"wavy"},{"noise":"meow","tail":"slinky"},{"noise":"bark","tail":"short"},{"noise":"meow","tail":"short"}]}
[$.pets[4]: should be valid to one and only one of the schemas ]
My Json Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"userInfo": {
"type": "object",
"properties": {
"firstName": { "type": "string" },
"lastName": { "type": "string" },
"emailAddress":{ "type": "string" }
},
"required": ["firstName", "lastName", "emailAddress"]
},
"userPassword": {
"type": "object",
"properties": {
"password": { "type": "string" },
"confirmPassword": { "type": "string" }
}
}
},
"type": "object",
"properties": {
"standaloneDeveloper": {
"$ref": "#/definitions/userInfo",
"$ref": "#/definitions/userPassword"
}
}
}
Data is always getting overwritten with #/definitions/userPassword
I am getting the following output with this schema
{
"standaloneDeveloper": {
"password": "ABCDEFGHIJKLMNOPQRSTUVWXYZABC",
"confirmPassword": "ABCDEFGHIJKLMNOPQRSTUVWXYZABC"
}
}
Expected output
{
"standaloneDeveloper": {
"firstName": "ABCDEFGHIJKLMNOPQRSTUVWXYZABC",
"lastName": "ABCDEFGHIJKLMNOPQRSTUVWXYZABC",
"emailAddress": "ABCDEFGHI",
"password": "ABCDEFGHIJKLMNOPQRSTUVWXYZABC",
"confirmPassword": "ABCDEFGHIJKLMNOPQRSTUVWXYZABC"
}
}
How can I combine userInfo and userPassword?
In JSON (and therefore JSON Schema as well) you can't have duplicate property names. You can use allOf to get around this.
"properties": {
"standaloneDeveloper": {
"allOf": [
{ "$ref": "#/definitions/userInfo" },
{ "$ref": "#/definitions/userPassword" }
]
}
}
This way each object has only one $ref in it.
I have json shown below. I want to get it work against a list of zoo which will must have zoo_unique_code. But can have animal or bird or both or none of them. But i want to validate it with sub schema if it have animal or bird e.g bird/animal_id. It seems subschema is not working.
{
"type": "object",
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"animal_id": {
"type": "string",
"maxLength": 24
},
"bird_id": {
"type": "string",
"maxLength": 50
},
"zoo_bird_and_animal": {
"type": "object",
"anyOf": [{
"properties": {
"zoo_bird": {
"type": "object",
"required": [
"zoo_bird_id"
],
"properties": {
"zoo_bird_id": {
"$ref": "#/definitions/bird_id"
}
}
}
}
}, {
"properties": {
"zoo_animal": {
"type": "object",
"required": [
"zoo_animal_id"
],
"properties": {
"zoo_animal_id": {
"$ref": "#/definitions/animal_id"
}
}
}
}
}
]
}
},
"properties": {
"zoo_list": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": [
"zoo_unique_code"
],
"minProperties": 1,
"properties": {
"zoo_unique_code": {
"type": "string",
"enum": [
"NEWYORKZOO",
"PARISZOO"
]
}
},
"$ref": "#/definitions/zoo_bird_and_animal"
}
}
}
}
And testing it with
{
"zoo_list": [
{
"zoo_unique_code": "NEWYORKCODE",
"zoo_bird": {
"zoo_bird_id": "newid"
}
}
]
}
Any suggestion will be appreciated.
As far as i can interpret your schema, it seems you want to use a combining schema at the end, rather then having the ref in the same items part.
With this allOf the schema needs to be a valid object like defined in items and like the ref in the definitions
Also the other error comes from using anyOf instead of allOf.
With anyOf, it needs to be valid against either the first or the second of the schemas, as both validate against a object, even when the first is invalid, the second is valid, so everything is valid. This could also be changed with additionalProperties, but then it does not work the way you nested it.
anyOf: As long as a value validates against either of these schemas, it is considered valid against the entire combined schema.
- combining-schemas
You would also want to use allOf here, so it must validate against all, or rewrite this condition to not use an object here.
With that anyOf to allOf modification, your given data now also validates the bird_id:
And i think you are not using draft-04 here, looks like draft-7.
{
"type": "object",
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"animal_id": {
"type": "string",
"maxLength": 24
},
"bird_id": {
"type": "string",
"maxLength": 50
},
"zoo_bird_and_animal": {
"type": "object",
"allOf": [
{
"properties": {
"zoo_bird": {
"type": "object",
"required": [
"zoo_bird_id"
],
"properties": {
"zoo_bird_id": {
"$ref": "#/definitions/bird_id"
}
}
}
}
},
{
"properties": {
"zoo_animal": {
"type": "object",
"required": [
"zoo_animal_id"
],
"properties": {
"zoo_animal_id": {
"$ref": "#/definitions/animal_id"
}
}
}
}
}
]
}
},
"properties": {
"zoo_list": {
"type": "array",
"minItems": 1,
"items": {
"allOf": [
{
"type": "object",
"required": [
"zoo_unique_code"
],
"minProperties": 1,
"properties": {
"zoo_unique_code": {
"type": "string",
"enum": [
"NEWYORKZOO",
"PARISZOO"
]
}
}
},
{
"$ref": "#/definitions/zoo_bird_and_animal"
}
]
}
}
}
}
Invalid data, see: invalid:
{
"zoo_list": [
{
"zoo_unique_code": "NEWYORKCODE",
"zoo_bird": {
"zoo_bird_id": "newidnewidnewidnewidnewidnewidnewnewidnewidnewidnewidnewidnewidnew"
}
}
]
}
Valid data, see valid:
{
"zoo_list": [
{
"zoo_unique_code": "NEWYORKZOO",
"zoo_bird": {
"zoo_bird_id": "newid"
}
}
]
}