jsonschema with custom property - jsonschema

I want to define a JsonSchema with a customProperty in it, this property follows some rules, so in order to validate those, I need to define a JsonSchema that will validate it.
So far I've managed to describe it properly, but it only works for the first level of attributes, and I want it to be recursive...
From my comprehension it should work, I probably made a mistake I'm unable to see and at this point I don't know if it's a bug, impossible, or stupid...
I believe redefining every type should be a possibility , but obviously I'd rather not.
Here is an example of the Json I want to validate
{
"title": "TheObject",
"type": "object",
"properties": {
"aString": {
"type": "string",
"myCustomProperty": {}
},
"anObjet": {
"type": "object",
"myCustomProperty": {},
"properties": {
"anotherObject": {
"type": "object",
"myCustomProperty": {}, //if this line is removed it still validates which I don't want
"properties": {}
}
}
}
}
}
and here is the JsonSchema I've done so far:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"title": {"type": "string"},
"type": {"type": "string","enum": ["object"]},
"properties": {
"type": "object",
"patternProperties": {
".*": {
"$ref": "#/definitions/Field"
}
}
}
},
"definitions": {
"Field": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"myCustomProperty": {
"$ref": "#/definitions/myCustomProperty"
},
"patternProperties": {
"^(?!myCustomProperty).*": {
"$ref": "#/definitions/Field"
}
}
},
"required": [
"type",
"myCustomProperty"
]
},
"myCustomProperty": {
//Some rules
}
}
}

I found a solution, I wasn't far from what I wanted after all.
In my definition of "Field", I am describing an object that defines an object, and I was missing the "properties" field. In which I had to put my recursive reference.
The right jsonSchema is the following :
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"title": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"object"
]
},
"properties": {
"type": "object",
"patternProperties": {
".*": {
"$ref": "#/definitions/Field"
}
}
}
},
"definitions": {
"Field": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"myCustomProperty": {
"$ref": "#/definitions/myCustomProperty"
},
"properties": { <==================== here
"type": "object",
"patternProperties": {
".*": {
"$ref": "#/definitions/Field"
}
}
}
},
"required": [
"type",
"myCustomProperty"
]
},
"myCustomProperty": {/*rules*/}
}
}
So far it works as expected, but if someone has a more elegant suggestion please share!

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.

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.

Json Schema dependency on outer field

Given a json structure like this
{
"name": "John Doe",
"billing_address": "123 main st",
"payment_details":{"credit_card": 55555555}
}
I need to make 'billing_address' a required field if payment_details.credit_card exists.
I've started from this example that achieves so if both fields were at the same level of nesting
{
"type": "object",
"properties": {
"name": { "type": "string" },
"credit_card": { "type": "number" }
},
"required": ["name"],
"dependencies": {
"credit_card": {
"properties": {
"billing_address": { "type": "string" }
},
"required": ["billing_address"]
}
}
}
However, what is the syntax to require this field that is one level above the dependant field?
That's too complicated for 'dependencies', so we go back to an if/then/else clause for that. We place these keywords at the top level, where the required field needs to be. In pseudocode: "if there is a payment_details property present, and it has a credit_card property, then require billing_address."
{
...,
"if": {
"type": "object",
"required": [ "payment_details" ],
"properties": {
"payment_details": {
"type": "object",
"required": [ "credit_card" ]
}
},
"then": {
"type": "object",
"required": [ "billing_address" ]
}
}
Note that the "type" and "required" keywords are required here -- as data of different types (for example an array) will cause object-specific keywords like "properties" and "required" to always evaluate to true.
thanks Ether! this worked, but only after adding the credit card as a property, in the if section.
the full if statement looks like this
"if": {
"type": "object",
"required": ["payment_details"],
"properties": {
"payment_details": {
"type": "object",
"properties": {
"credit_card": {}
},
"required": ["credit_card"]
}}},
the full json schema looks like this
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"billing_address": {
"type": "string"
},
"payment_details": {
"type": "object",
"properties": {
"credit_card": {
"type": "integer"
}
}
}
}
,
"if": {
"type": "object",
"required": ["payment_details"],
"properties": {
"payment_details": {
"type": "object",
"properties": {
"credit_card": {}
},
"required": ["credit_card"]
}}},
"then": {
"type": "object",
"required": [ "billing_address" ]
}
}

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
}

JSON schema for object with either A and B or C required properties

I have two possible JSON objects for one request:
{
"from": "string",
"to": "string",
"text": "string"
}
or
{
"number": "integer",
"text": "string"
}
In both cases "text" property is optional. Other properties are required (either "number, or both "from" and "to").
What will be the correct JSON schema to validate this?
Here is another solution that I think is a bit more clear. The dependencies clause ensures that "from" and "to" always come as a pair. Then the oneOf clause can be really simple and avoid the not-required boilerplate.
{
"type": "object",
"properties": {
"from": { "type": "string" },
"to": { "type": "string" },
"number": { "type": "integer" },
"text": { "type": "string" }
},
"dependencies": {
"from": ["to"],
"to": ["from"]
},
"oneOf": [
{ "required": ["from"] },
{ "required": ["number"] }
]
}
Finally managed to build the correct scheme.
{
"definitions": {
"interval": {
"type": "object",
"properties": {
"from": {
"type": "string"
},
"to": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": ["from", "to"],
"not": {
"required": ["number"]
}
},
"top": {
"type": "object",
"properties": {
"number": {
"type": "integer"
},
"text": {
"type": "string"
}
},
"required": ["number"],
"allOf": [
{
"not": {
"required": ["from"]
}
},
{
"not": {
"required": ["to"]
}
}
]
}
},
"type": "object",
"oneOf": [
{"$ref": "#/definitions/interval"},
{"$ref": "#/definitions/top"}
]
}