Having multiple properties in json schema items - jsonschema

I am trying to write the schema to have multiple properties, but unable to do so.
I tried to add another ref in additional properties but it also did not work.
It would be helpful if someone can point the right way to do it.
Trying to write JSON schema for :
[{
"name": "hi",
"metaData": {
"value": true
}
}, {
"name": "hello",
"metaData": {
"value": true
}
}]
the JSON schema I am trying to use is :
{
"definitions": {
"metadata":
{
"type":"object",
"properties":{
"value":{"type":"boolean"}
}
}
},
"title": "MetaData",
"type": "array",
"items": {
"name":
{
"type":"string"
},
"additionalProperties": {
"$ref": "#/definitions/metadata"
}
},
}

I got it working by using the following schema :
{
"definitions": {
"metadata":
{
"type":"object",
"properties":{
"value":{"type":"boolean"}
}
}
},
"title": "MetaData",
"type": "array",
"items": {
"type":"object",
"properties": {
"name": {
"type": "string"
}
},
"additionalProperties": {
"$ref": "#/definitions/metadata"
}
},
}

Related

Add additional properties to json array member schema

I'm trying to create 2 separate schemas for some json files which serve as scripts.
One schema should contain all commands possible, while another one should only contain a subset of commands.
My thinking was I create minimal schema first:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"$schema": {},
"commands": {
"type": "array",
"items":
{
"title": "One step of the migration",
"type": "object",
"minProperties": 1,
"maxProperties": 1,
"properties": {
"simple_command_1": {"type": "object"},
"simple_command_2": {"type": "object"},
}
}
}
}
}
which would validate json files like this:
{
"$schema": "../../migration_schema_v1.json",
"commands": [
{"simple_command_1": {}},
{"simple_command_1": {}},
{"simple_command_2": {}},
{"simple_command_2": {}},
]
I'm trying to avoid using the word extend but... I want to be able to create a second schema that has every command the first one has and an additional advanced_command_3. So I created this:
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$ref": "/migration_schema_v1.json#/",
"type": "object",
"properties": {
"foo": {
"type": "object",
"title": "this does work"
},
"commands": {
"items":
{
"properties": {
"advanced_command_3": {"type": "object"}
}
}
}
}
}
I'm able to refer this second schema, and it does inherit everything from the first one, but I'm not really able to add any properties (commands)
I was however able to add additional properties in the root, next to "commands". See "foo" property.
Using allOf[] or $ref in the sub-property doesn't seem to make a difference.
Am I getting this all wrong?
Thanks!
I would actually break this into three schemas. The first contains the primary definition for your data, including all of the valid commands.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/definitions",
"type": "object",
"properties": {
"$schema": {},
"commands": {
"type": "array",
"items":
{
"title": "One step of the migration",
"type": "object",
"minProperties": 1,
"maxProperties": 1,
"properties": {
"simple_command_1": {"type": "object"},
"simple_command_2": {"type": "object"},
"advanced_command_3": {"type": "object"}
}
}
}
}
}
The other two are your simple and advanced schemas. They work by defining that only the possibilities of the commands you want are required then referencing back to the definition schema to get the rest of the requirements.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/simple",
"$ref": "definitions",
"properties": {
"commands": {
"items": {
"anyOf": [
{ "required": ["simple_command_1"] },
{ "required": ["simple_command_2"] }
]
}
}
}
}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/advanced",
"$ref": "definitions",
"properties": {
"commands": {
"items": {
"anyOf": [
{ "required": ["simple_command_1"] },
{ "required": ["simple_command_2"] },
{ "required": ["advanced_command_3"] }
]
}
}
}
}
I think this setup contains minimal repetition.
At this point you might be able to get away with what you're wanting to do with the extends thing. You'd have to extract the commands requirements in the simple schema into a $defs keyword and reference them from the advanced schema.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/simple",
"$defs": {
"simpleCommands": {
"anyOf": [
{ "required": ["simple_command_1"] },
{ "required": ["simple_command_2"] }
]
}
},
"$ref": "definitions",
"type": "object",
"properties": {
"commands": {
"items": { "$ref": "#/$defs/simpleCommands" }
}
}
}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/advanced",
"$ref": "definitions",
"type": "object",
"properties": {
"commands": {
"items": {
"anyOf": [
{ "$ref" : "simple#/$defs/simpleCommands" },
{ "required": ["advanced_command_3"] }
]
}
}
}
}
I haven't tested this though.

Referring inner properties in the IF condition - JSON Schema

The following is a sample schema to depict the issue
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"definitions": {
"person": {
"type": "object",
"properties": {
"age": {
"type": "string"
}
}
}
},
"properties": {
"child": {
"$ref": "#/definitions/person"
}
},
"required": [
"child"
],
"if": {
"properties": {
"person/age": {
"const": "3"
}
}
},
"then": {
"properties": {
"guardian": {
"$ref": "#/definitions/person"
}
},
"required": [
"guardian"
]
}
}
Is there a way to refer age inside the person object?
{"child":{"age":"3"}}. Should fail as guardian tag is missing
The above data should fail as the guardian object is missing.
Remember that if is just a regular schema validating against the instance. Just nest your properties like you would with any nested object structure.
{
"type": "object",
"properties": {
"child": {
"type": "object",
"properties": {
"age": { "const": "3" }
},
"required": ["age"]
}
},
"required": ["child"]
}
Note that the type and required keywords are necessary to not inadvertently trigger the then schema. For example, with out them, these would cause the then to trigger when you probably didn't want it to.
{}
{ "child": null }
{ "child": {} }

Does JSON Schema have a switch like structure?

Consider this following example,
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Animal",
"type": "object",
"properties": {
"type": {
"type": "string",
"description": "Type of animal."
},
"data": {
"$ref": "#/$definations/cat"
}
},
"$definations":{
"cat" : {
"properties" : {
"meow" : {
"type": "string"
}
}
}
},
"required": ["type"]
}
and the correct JSON is ,
{
"type" : "cat",
"data" : {
"meow" : "OK"
}
}
Now I am having enum of Animals, and the data ref will vary based on type of Animal.
I have have tried if else but it seems not efficient as the condition will keep on growing.
Also used anyOf but how will I make sure that meow will always belong to animal type cat and not dog.
Can we have something like,
cat : { "$ref" : "#/$definations/cat" },
dog : { "$ref" : "#/$definations/dog" }
EDIT : Or dynamic value in ref like #/$definations/{type-value} ?
Thanks in advance.
I have have tried if else but it seems not efficient as the condition
will keep on growing.
Can we have something like...
No. JSON Schema (2019-09 and previous) doesn't have a "switch".
You'll need to use allOf to create multiple if then conditions.
After #Relequestual's response and some more digging I found there is no such way. At least in this version, fingers crossed for the future releases.
Here is my solution, feel free to suggest improvements.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Animal",
"type": "object",
"properties": {
"type": {
"type": "string",
"description": "Type of animal."
}
},
"$definations": {
"cat": {
"properties": {
"meow": {
"type": "string"
}
},
"required": [
"meow"
]
},
"dog": {
"properties": {
"bhow": {
"type": "string"
}
},
"required": [
"bhow"
]
}
},
"oneOf": [
{
"properties": {
"type": {
"const": "cat"
},
"data": {
"$ref": "#/$definations/cat"
}
}
},
{
"properties": {
"type": {
"const": "dog"
},
"data": {
"$ref": "#/$definations/dog"
}
}
}
],
"required": [
"type"
]
}

Json property structure dependant of another property

I've been working on a json schema to validate the answers from one of my webservices.
The answer is splitted in two properties: data and status. If status.code is set to 0, then data will have to respect a specific schema. Else, if status.code is set to -1, data won't be read, so I don't want to check if it respects the schema.
Here is the schema :
{
"$schema": "http://json-schema.org/schema#",
"id": "file://registration.json",
"type": "object",
"properties": {
"status": {
"$ref": "#/definitions/classes/status"
}
},
"anyOf": [
{
"$ref": "#/definitions/conditions/status-is-ok"
},
{
"$ref": "#/definitions/conditions/status-is-nok"
}
],
"definitions": {
"classes": {
"status": {
"type": "object",
"properties": {
"code": {
"type": "integer"
},
"message": {
"type": "string"
}
},
"required": [
"code",
"message"
]
},
"data": {
"type": "object",
"properties": {
"propertyA": {
"type": "#/definitions/classes/metadatauser"
},
"propertyB": {
"type": "#/definitions/classes/membreinfo"
}
},
"required": ["propertyA", "propertyB"]
}
},
"conditions": {
"status-is-ok": {
"status": {
"properties": {
"code": 0
}
},
"data": {
"$ref": "#/definitions/classes/data"
}
},
"status-is-nok": {
"status": {
"properties": {
"code": -1
}
},
"data": {
"type": "object"
}
}
}
}
}
And here's an example of what should not be validated:
{
"data": {},
"status": {
"code": 0,
"message": "OK"
}
}
At the moment, this portion of code passes, and I don't know why.
You've got a few things wrong here, so I'll try to explain all of them. You were on the right track!
"properties": {
"code": 0
}
The value of "properties" MUST be an object. Each value of this object
MUST be a valid JSON Schema.
http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.5.4
You can't put the value you expect as the value for a property key.
You CAN however use the [const]1 keyword to achive a specific value validation.
"$ref": "#/definitions/conditions/status-is-ok"
...
"conditions": {
"status-is-ok": {
"status": {
"properties": {
[The definitions] keyword's value MUST be an object. Each member value of this
object MUST be a valid JSON Schema.
https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-9
This means that you need to treat each value of each key in a defintions as a JSON Schema. If you had a JSON Schema where you did not nest "status" inside a properties object, no validation would take place. The same is true for "data".
(Strictly, according to the definitions section of the spec, you MUST NOT nest schemas deeply in the definitions object, but this seems to be supported by some implementations anyway, and resolves using the correct resolution rules. Prefixing may be better.)
The complete fixed schema is as follows.
{
"$schema": "http://json-schema.org/schema#",
"id": "file://registration.json",
"type": "object",
"properties": {
"status": {
"$ref": "#/definitions/classes/status"
}
},
"anyOf": [
{
"$ref": "#/definitions/conditions/status-is-ok"
},
{
"$ref": "#/definitions/conditions/status-is-nok"
}
],
"definitions": {
"classes": {
"status": {
"type": "object",
"properties": {
"code": {
"type": "integer"
},
"message": {
"type": "string"
}
},
"required": [
"code",
"message"
]
},
"data": {
"type": "object",
"properties": {
},
"required": [
"propertyA",
"propertyB"
]
}
},
"conditions": {
"status-is-ok": {
"properties": {
"status": {
"properties": {
"code": {
"const": 0
}
}
},
"data": {
"$ref": "#/definitions/classes/data"
},
},
"additionalProperties": false
},
"status-is-nok": {
"properties": {
"status": {
"properties": {
"code": {
"const": -1
}
}
},
"data": {
"type": "object"
},
},
"additionalProperties": false
}
}
}
}
Please do let me know if any of this doesn't make sense.
Feel free to join the JSON Schema slack server should you want to discuss any aspects further! Happy to also comment here.

Json Schema: Require a property only when a specific property is present in a deep nested object

I need to build a json schema (draft 4) that requires a property based on the presence of a property in another nested object. I already searched and tried a lot of things (anyOf, oneOf, not, dependencies) with no luck.
Maybe this is not possible to in json schema?
This is my simplified schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"required": ["dog"],
"properties": {
"dog": {
"type": "object",
"required": ["bananas"],
"properties": {
"bananas": { "$ref": "bananas.json" },
"thing": {
"type": "object",
"properties": {
"total": { "type": "string" }
}
}
}
}
}
}
And this is bananas.json
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"required": ["banana"],
"definitions": {
"non-empty-string": {
"type": "string",
"minLength": 1
}
},
"properties": {
"banana": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["unit"],
"properties": {
"unit": { "type": "string" },
"thing": {
"type": "object",
"anyOf": [
{ "required": [ "tax_transfers" ] },
{ "required": [ "tax_retentions" ] }
],
"properties": {
"tax_transfers": {
"type": "object",
"required": ["tax_transfer"],
"properties": {
"tax_transfer": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"properties": {
"rate": { "type": "string" }
}
}
}
}
},
"tax_retentions": {
"type": "object",
"required": ["tax_retention"],
"properties": {
"tax_retention": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"properties": {
"rate": { "type": "string" }
}
}
}
}
}
}
}
}
}
}
}
}
I need that when one or more objects in the array have a 'thing' property (at bananas -> banana -> thing).
Then the property 'thing' at (dog -> thing) should be required.
Any help would be really appreciated.
You need two things to express your constraint. The first is "contains" and the other is "implication". I've organized each in the definitions section.
Contains
The items keyword allows us to require that all items in an array are valid against a schema. If it is not true that all of the items in the array are not valid against the schema, then we know that at least one item is valid.
{
"not": {
"items": { "not": { ... schema ... } }
}
}
If you are able to upgrade to JSON Schema draft-06, a contains keyword was added to make this much easier.
{
"contains": { ... schema ... }
}
Implication
Implication allows you to do something like a conditional. The condition schema implies the constraint schema if either the condition is true, or the constraint is true (or both are true). It's effectively the same as saying, if the condition is true then the constraint must also be true.
{
"anyOf": [
{ "not": { ... condition schema ... } },
{ ... constraint schema ... }
]
}
JSON Schema draft-07 adds the if-then-else keywords in attempt to address this case better. I personally dislike the way this was done enough that I'll stick with the implication pattern for this kind of thing, but here it is in case you want to try it.
{
"if": { ... schema ... },
"then": { ... schema ... },
"else": { ... schema ... }
}
All together
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"required": ["dog"],
"properties": {
"dog": {
"type": "object",
"required": ["bananas"],
"properties": {
"bananas": { "$ref": "bananas.json" },
"thing": { "type": "object" }
}
}
},
"allOf": [
{ "$ref": "#/definitions/banana-things-implies-dog-things" }
],
"definitions": {
"banana-has-things": {
"properties": {
"dog": {
"properties": {
"bananas": {
"properties": {
"banana": {
"not": {
"items": { "not": { "required": ["things"] } }
}
}
}
}
}
}
}
},
"banana-things-implies-dog-things": {
"anyOf": [
{ "not": { "$ref": "#/definitions/banana-has-things" }},
{
"properties": {
"dog": { "required": ["things"] }
}
}
]
}
}
}