json schema object property constraints - jsonschema

In my schema I have an array of phone objects. Each object has a "status" property, which can be one of three values: "Primary", "Active" and "Not-in-use".
I want to set the following constraint:
If the number of phone objects > 0 then exactly one must have status="Primary"
Is this possible with json schema? If so, how?

This schema is pretty close to what you want. The only restriction is that the "Primary" phone number needs to be the first item in the array.
You might be able to get "Primary" to be anywhere in the array with some creative use of not. I'll update the answer if I figure it out.
{
"type": "object",
"properties": {
"phoneNumbers": {
"type": "array",
"items": [{ "$ref": "#/definitions/primaryPhone" }],
"additionalItems": { "$ref": "#/definitions/additionalPhone" }
}
},
"definitions": {
"phone": {
"type": "object",
"properties": {
"label": { "type": "string" },
"number": { "type": "string" }
},
"required": ["label", "number", "status"]
},
"primaryPhone": {
"allOf": [{ "$ref": "#/definitions/phone" }],
"properties": {
"status": { "enum": ["Primary"] }
}
},
"additionalPhone": {
"allOf": [{ "$ref": "#/definitions/phone" }],
"properties": {
"status": { "enum": ["Active", "Not-in-use"] }
}
}
}
}

Related

Cannot find classification schema reference in JSON Schema

I have a product schema which tries to reference an id in my document. It is a common reference to multiple objects. Unfortunately, my ide claims classification reference cannot be found. I am very new to json schemas and find only snippets which don't quite show how the references are supposed to work. Here is my schema.
{
"$schema": "https://json-schema.org/draft-07/schema#",
"$id": "https://digital.com/schemas/products",
"description": "Schema for Product Data",
"title": "Products",
"type": "object",
"required": ["products"],
"properties": {
"products": {
"type": "array"
},
"options": {
"type": "array",
"items": {
"type": "object",
"properties": {
"productId": {
"type": "string"
},
"productName": {
"type": "string"
},
"categories": {
"type": "object",
"additionalProperties": false,
"allOf": [
{ "$ref":"/products/classification" }
]
},
"productType": {
"type": "string",
"enum": ["electronic", "digital", "internet", "video"]
}
},
"required": ["productId"]
}
},
"classification": {
"$id": "/products/classification",
"type":"object",
"properties": {
"relevance-score": {
"type":"integer",
"minimum": 1,
"maximum": 5
},
"group":{
"enum":["adult","teen","seniors"]
}
}
},
"definitions": {
"mp4": {
"type": "object",
"properties": {
"mediaType": {
"type": "string",
"enum": ["video"]
},
"playlength": {
"type": "integer"
}
}
}
},
"mp3": {
"type": "object",
"properties": {
"mediaType": {
"enum": ["audio"]
}
}
}
}
}
I have defined a classification object to use in the class property of categories object like this
"$id":"/products/classification".
I tried setting it relative to the $id at the top of the document which is "https://digital.com/schemas/products" but I'm sure I haven't set it correctly. I want to use the classification object in the allOf property of the categories object.
Your reference is "$ref": "/products/classification". Where is this supposed to point?
Odds are the validator you're using is looking for a schema with the $id of "https://digital.com/products/classification". If the validator doesn't know about this schema (or perhaps is searching for that file), it can't get to it.
The other possibility is that you intend this to be a JSON Pointer. If that's the case, it needs to be URI-formatted: "#/products/classification"
However, this location doesn't existin within your schema, so it'll need to be updated.

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": {} }

How can I validate a json schema array that contains a MIXED type of objects?

I have searched and haven't quite found a solution.
I would like to do a schema as so:
...
"bag": {
"type": "array",
"items": {
"anyOf": [
{"$ref": "#/definitions/obj1"},
{"$ref": "#/definitions/obj2"},
{"$ref": "#/definitions/obj3"}
]
},
"required": ["items"],
"minItems": 1
}
...
With objects defined:
...
"definitions": {
"obj1": {
"type": "object",
"properties": {
"obj1": {
"type": "object",
"properties": {
"a": {
"type": "string"
}
},
"required": ["a"]
}
}
},
"obj2": {
"type": "object",
"properties": {
"obj1": {
"type": "object",
"properties": {
"b": {
"type": "string"
}
},
"required": ["b"]
}
}
},
"obj3": {
"type": "object",
"properties": {
"obj1": {
"type": "object",
"properties": {
"c": {
"type": "string"
}
},
"required": ["c"]
}
}
}
}
...
Ideally, I would like to validate against a schema that looks like this:
...
"bag": [
{
"obj1": {"a": "test1"}
},
{
"obj3": {"c": "test1"}
}
]
...
In this context, if someone passes obj1 and obj3 into bag. By the schema, obj1 requires property a and obj3 requires property c.
I'm having trouble actually executing this as the validation doesn't seem to enforce correctly.
Any tips? Thanks in advance.
From your current schema and example data, I can't tell exactly what you want, but making an educated guess...
I suspect you want to use oneOf as opposed to anyOf.
anyOf allows you to match multiple subschemas, and it looks like you only want to allow matching one of the subschemas, obj1, 2, or 3.
This would help you debug the issue, but it's not the cause of your always passing validation.
For each definition subschema, you need to add "additionalProperties": false.
Here's the key: JSON Schema is constraints based, meaning anything not constrained is allowed.
additionalProperties restricts the allowed properties of an object to those defined in properties (and patternProperties).
Here's the example schema. You can see it working with your instance here: https://jsonschema.dev/s/MjBUp
{
"$schema": "http://json-schema.org/draft-07/schema",
"definitions": {
"obj1": {
"type": "object",
"properties": {
"obj1": {
"type": "object",
"properties": {
"a": {
"type": "string"
}
},
"required": ["a"]
}
},
"additionalProperties": false
},
"obj2": {
"type": "object",
"properties": {
"obj1": {
"type": "object",
"properties": {
"b": {
"type": "string"
}
},
"required": ["b"]
}
},
"additionalProperties": false
}
},
"type": "array",
"items": {
"anyOf": [
{"$ref": "#/definitions/obj1"},
{"$ref": "#/definitions/obj2"}
]
},
"required": ["items"],
"minItems": 1
}

How to extent an object with 1 parameter in json-schema

I wrote one JSON schema before, but now, as I am trying to make it a bit more advanced I get stuck.
(I am open to 'good practice' tips in the comments)
(Is the $id optional? should I remove it for simplicity in the example code?)
Goal:
I am trying to make a schema with an object definition (example_obj) that is recursively used. This object may only have 1 argument (or or and or value). But in the root of the json, I want to add 1 additional property.
json-schema
{
"definitions": {
"example_obj": {
"$id": "#/definitions/example_obj",
"type": "object",
"maxProperties": 1,
"properties": {
"or": {
"$id": "#/definitions/example_obj/properties/or",
"type": "array",
"items": {
"$id": "#/definitions/example_obj/properties/or/items",
"$ref": "#/definitions/example_obj"
}
},
"and": {
"$id": "#/definitions/example_obj/properties/and",
"type": "array",
"items": {
"$id": "#/definitions/example_obj/properties/and/items",
"$ref": "#/definitions/example_obj"
}
},
"value": {
"$id": "#/definitions/example_obj/properties/value",
"type": "string"
}
}
}
},
"type": "object",
"title": "The Root Schema",
"required": ["filter_version"],
"allOf": [
{
"$ref": "#/definitions/example_obj"
},
{
"properties": {
"filter_version": {
"$id": "#/properties/filter_version",
"type": "string",
"pattern": "^([0-9]+\\.[0-9]+)$"
}
}
}
]
}
json which I want to pass validation:
{
"filter_version": "1.0",
"or": [
{
"and": [
{
"value": "subject"
}
]
},
{
"or": [
{
"value": "another subject"
}
]
}
]
}
Issue:
When I try to extend example_obj for the root definition it seems to fail because the example_obj object does not allow more then 1 property by design.
In other words, it appears that every check for the number of argument that I add to example_obj is also performed on the additional property (i.e. filter_version).
Does anyone know where to place this check for 'exactly 1 argument' so that it is not evaluated on the root object?
Attempts:
I tried working with different ways of determining the requirements of example_obj, but with no success. Like with replacing "maxProperties": 1 with:
"oneOf": [
{
"required": [
"or"
]
},
{
"required": [
"and"
]
},
{
"required": [
"where"
]
},
{
"required": [
"where not"
]
}
],
Thanks in advance for any help!!
Checking my schema with the online schema validator.
(In the end I need to validate it in Python, in case it matters)
You can use oneOf instead of maxProperties to get around this.
{
"type": "object",
"properties": {
"filter_version": {
"type": "string",
"pattern": "^([0-9]+\\.[0-9]+)$"
}
},
"required": ["filter_version"],
"allOf": [{ "$ref": "#/definitions/example_obj" }],
"definitions": {
"example_obj": {
"type": "object",
"properties": {
"or": { "$ref": "#/definitions/example-obj-array" },
"and": { "$ref": "#/definitions/example-obj-array" },
"value": { "type": "string" }
},
"oneOf": [
{ "required": ["or"] },
{ "required": ["and"] },
{ "required": ["value"] }
]
},
"example-obj-array": {
"type": "array",
"items": { "$ref": "#/definitions/example_obj" }
}
}
}
P.S. You are using $id wrong. I know there is a tool out there that generates schemas like this and causes this confusion. The way $id is used here is a no-op. It doesn't hurt, but it doesn't do anything other than bloating your schema.

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.