Need help :Json Schema Design - json

I am designing the the json scheme. and I am facing some issues while designing the schema.
Here is the problem.
I have an array of group objects. and I want this array should contain unique group objects. I want to make them unique based on object id ( ex group.id)
The groups array is not unique if (groups[0].id == groups[1].id) , I want to make unique only based on group id, Below is my Json structure.
"groups": {
"type": "array",
"items": {"$ref": "#/group"},
"uniqueItems":true
},
"group": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"type": {
"type": "string",
"enum": [
"a",
"b"
]
},
"command": {
"type": "string",
"enum": [
"add",
"modify"
]
}
}
},

Well, there is no magic bullet here. Remind Json-Schema is intended for defining structure of Json Data (not values).
One option would be not consider your groups node an "array" but instead an "object", and use additionalProperties to express that all additional properties should contain "type" and "command" properties.
Then you would use the name of each property in groups as id, so it would be unique.
The problem with this approach is that you do not restrict this id to be numeric (It might not be acceptable in your context). Even you could use patternProperties to match the "type,command" schema just to numeric "id's".

Related

Reusing JSON subschema

I am needing to use a sub schema multiple times in my JSON file, but haven't been able to figure out the correct way to structure the schema file such that I am able to get the schema validation on all the sub properties instead of just the property that I list in the schema file.
This question here was getting at a similar question, but the answer didn't make much sense/I wasn't sure if or how I could use the same method here. Am I thinking too much in the OOP mindset with multiple instances of a single class?
Here is more or less what I am trying to do
{
"Object1": {
"Title": "Some Title",
"Description": "Some Description"
},
"Object2": {
"Title": "Another title",
"Description": "Another Description"
}
// unknown number of objects but each object should have the same sub schema
}
Here is what I have thus far
{
"$id": "http://example.com/example.json",
"$schema": "http://json-schema.org/draft-07/schema",
"required": [
"Object1"
],
"title": "The root schema",
"type": "object",
"properties": {
"Object1": {
"required": [
"Title",
"Description"
],
"title": "The Reusable Object schema",
"type": "object",
"properties": {
"Title": {
"title": "The Title schema",
"type": "string"
},
"Description": {
"title": "The Description schema",
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": true
}
If all values of the object should follow the schema, the solution is quite simple.
First, you have to remember how additionalProperties works...
The value of "additionalProperties" MUST be a valid JSON Schema.
This keyword determines how child instances validate for objects,
and does not directly validate the immediate instance itself.
Validation with "additionalProperties" applies only to the child
values of instance names that do not match any names in "properties",
and do not match any regular expression in "patternProperties".
For all such properties, validation succeeds if the child instance
validates against the "additionalProperties" schema.
https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-6.5.6
So, now we know that additionalProperties takes a JSON Schema, and not just booleans (booleans are valid JSON Schema), the solution might be a little obvious.
Remove the outermost additionalPropertie, rename properties to additionalProperties, and remove the key Object1 and object braces.
The result is the following...
...
"title": "The root schema",
"type": "object",
"additionalProperties": {
"required": [
"Title",
"Description"
],
...
Live demo: https://jsonschema.dev/s/pqwCc
I don't know what you would want to do with the outer most required though. I guess remove it, as you don't know in advance what the keys will be.
Maybe you want to use minProperties to make sure there is at least one?

validate nested content of json schema

I’m creating a schema consists of multiple categories. In each category there’s an array of key:value pairs. Each key represents the display name of value. Each value is unique and can only be assigned to a single key and category.
As an example, a category called ‘primates’ will have ‘human’ as one of the key/ display name, and the biological name ‘Homo sapiens’ will be the corresponding value of the key:value pair.
I want to validate data entry so that data matches with only one of the key/ display name. I put anyOf for the categories, does it do the same job? Is this how you will arrange items in the schema?
{
"$schema": "https://example.com/schema/dictionary",
"$id": "https://example.com/schemaoutput/dictionary",
"description": "A schema that validates the minimum requirements for validation output",
"type": "array",
"items": {
"additionalProperties": false,
"properties": {
"subcat1": {
"type": "string",
"title": "category1",
"tag": [
{
"display_labelA": [
"class_A"
],
"display_labelB": [
"class_B"
]
}
]
},
"subcat2": {
"type": "string",
"title": "category2",
"tag": [
{
"display_labelC": [
"class_C"
],
"display_labelD": [
"class_D"
]
}
]
}
},
"anyOf": [
{
"required": [
"subcat1",
"subcat2"
]
}
]
}
}
Edit:
As requested I updated the post with expected pass and fail scenarios. For example I want to create a json schema containing different categories of animals in the animal kingdom. Each key:value pair refer to the commonly known animal name and the corresponding scientific name. Only data entries of animals' commonly known names will be accepted.
Pass scenarios:
A data entry ‘human’ will be accepted, since it is one of the key:value pair (human: Homo sapiens) of category ‘primates’.
A data entry ‘chimpanzee’ will also be accepted. It is also one of the key:value pair (chimpanzee: pan troglodytes) of category ‘primates’.
A data entry ‘salmon’ will be accepted. Its key:value pair (Salmon: Salmo salar Linnaeus) is located in another category ‘fish’.
Fail scenarios:
Any data entry that are not listed in the animal kingdom dictionary will not be accepted e.g. Pear, Oranges, Table, Chairs…
Looking through 1 and other posts I think enum is what I need to map key:value pairs (i.e. biological name:commonly known name) under each category (primates or fish). I'm using anyOf at the end of the JSON schema to validate data entry that contain any values specified under any category.
{
"$schema": "https://example.com/schema/dictionary",
"$id": "https://example.com/schemaoutput/dictionary",
"description": "A schema that validates the minimum requirements for validation output",
"type": "array",
"items": {
"additionalProperties": false,
"properties": {
"subcat1": {
"type": "string",
"title": "category1",
"enum": [{
"display_labelA": "class_A",
"display_labelB": "class_B"
}]
},
"subcat2": {
"type": "string",
"title": "category2",
"enum": [{
"display_labelA": "class_A",
"display_labelB": "class_B"
}]
}
},
"anyOf": [{
"required": [
"subcat1",
"subcat2"
]
}]
}
}

Apply required field to referenced JSON data schema

I have the following use-case I try to solve with JSON schemas.
I have a generic JSON data schema for, for example, a user. Here is an example of the user.schema.json file.
{
"type": "object",
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"email": {
"type": "string",
"minLength": 1
},
"locale": {
"type": "string",
"minLength": 1
},
"active": {
"type": "boolean",
"default": true
},
"password": {
"type": "string",
"minLength": 8
},
"roles": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
}
}
}
Now I have 2 different kinds of requests:
- POST: Add a user
- PATCH: Update user data.
In 1 case, I can send this data structure, with 3 required fields, while in case of a patch each field is optional.
So I get the post request file: post-user.schema.json:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "user.schema.json",
"required": [
"name",
"password",
"email"
]
}
And for my patch (path-user.schema.json:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "user.schema.json"
}
Now the issue that I am having is that my POST schema also marks a user like:
{
"name": "NoPassword",
"email": "nopassword#moba.nl",
"roles": []
}
Which is missing the required password field, as a valid JSON schema.
Apparently, this is not the way to assign required fields to a referenced data structure. I have tried to use google to see what I can find on the subject regarding this using searches like:
[ how to assign required field to referenced schema's ]
and I tried to obtain this info from the documentation.
I have no luck.
My questions now are:
A. Is it possible to assign required fields to a $referenced json schema data object.
B. If this is possible how to do it
C. If this is not possible, what would be a good way to approach this.
Any help is much appreciated.
Using $ref results in all other properties in the object being ignored, so you need to wrap your use of $ref.
Let's take a look at the spec:
An object schema with a "$ref" property MUST be interpreted as a
"$ref" reference. The value of the "$ref" property MUST be a URI
Reference. Resolved against the current URI base, it identifies the
URI of a schema to use. All other properties in a "$ref" object MUST
be ignored.
https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-01#section-8.3
Then consider the schema you included in your question:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "user.schema.json",
"required": [
"name",
"password",
"email"
]
}
Reading the spec, you can see why required will be ignored.
Originally $ref was only designed to replace a WHOLE object, not ADD to the conditions for the object.
What you want is for multiple schemas to be applied to the instance. To do this, you use allOf.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"allOf": [
{
"$ref": "user.schema.json"
},
{
"required": [
"name",
"password",
"email"
]
}
]
}
I loaded this schema into a demo for you to test at https://jsonschema.dev - although it doesn't support references yet, so I transcluded the reference, but the validation will work the same.
From draft-8 onwards, $ref will behave as you expect, as it becomes an applicator keyword rather than a keyword with special behaviours, meaning other keywords in the same object will not need to be ignored.

JSON Schema require a specific array element

I defined a list of attributes as json schema:
{
"$schema": "http://json-schema.org/schema#",
"type": "object",
"definitions": {
"attribute": {
"type": "object",
"properties": {
"symbolic-name": { "type":"string"},
"value": { "type":"string"}
},
"required": ["symbolic-name", "value"]
},
"displayname": {
"type": "object",
"properties": {
"symbolic-name": {"enum":["displayName"]},
"value": { "type":"string"}
},
"required": ["symbolic-name", "value"]
}
},
"properties": {
"attributes":{
"type": "array",
// This is the crucial point:
"items": {"oneOf": [
{"$ref": "#/definitions/attribute"},
{"$ref": "#/definitions/displayname"}
]},
"uniqueItems": true
}
}
}
I want to require the list to have exactly one attribute with symbolic-name="displayName"
A valid data object would be:
{
"attributes":[
{"symbolic-name": "displayName", "value": "Display Name"},
{"symbolic-name": "somethingElse", "value": "value1"}
{"symbolic-name": "somethingElse", "value": "value2"}
]
}
Now, this fails to validate as the displayName attribute does not only match "oneOf", but both restrictions. I cannot change it to "allOf", since then all other attributes beside displayName won't match anymore.
In order for your "oneOf" to work, you need your "attribute" and "displayname" schemas to be mutually exclusive- as written, anything that is a valid "displayname" is also a valid "attribute". We can do that by excluding "displayName" as a valid symbolic name for "attribute":
"symbolic-name": {
"type": "string",
"not": {"enum": ["displayName"]}
}
Now elements with a symbolic name of "displayName" can match the "displayname" definition, but will never match the "attribute" definition.
The other part of your question is about having exactly one "displayname" in your array. This is trickier. It also depends on what draft of JSON Schema you are using. 4 and 6 are implemented, and 7 was released on Monday- just declaring "$schema": "http://json-schema.org/schema#" means you are using the most recent one, which would be 7. I recommend using a specific draft for $schema instead of the non-numbered one which may change without notice.
If you are OK with requiring the "displayname" to be the first element of the array, then this would work in any draft (and you don't even need the "oneOf"):
"items": [{"$ref": "#/definitions/displayname"}],
"additionalItems": {"$ref": "#/definitions/attribute"}
Note that "items" is an array here. This means that the first item MUST be a "displayname" and all additional items beyond that first item MUST be "attribute"s.
If you want to allow the "displayname" at any position, that's harder. As of draft-06 there is "contains", which requires at least one item to match the given schema. But there is no easy way to say "at most one item". However, "minContains" and "maxContains" have been suggested for draft-08: https://github.com/json-schema-org/json-schema-spec/issues/441
For now, hopefully you are OK with requiring the first position to be the "displayname", as that will work in all drafts.

How to validate for nullable types using json schema validator?

I'm using the play-json-schema-validator and want to set up an integration test with scala in order to check an API's JSON response schema.
Certain fields of the response are nullable and I want to validate for that. So some field can be either a string or null yet it can never be a number.
Playing around on its playground I want to validate for an array of objects that each object's name property is either a string or null.
I came up with this schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product set",
"type": "array",
"items": {
"title": "Product",
"type": "object",
"properties": {
"name": {
"type": ["string", null]
}
}
}
}
Yet though it validates the string and null case, I now get a false positive for numbers. I was expecting an error for this json, yet it validates:
[
{
"name": "Red anger"
},
{
"name": null
},
{
"name": 13
}
]
How to declare a field of a type as nullable using schema validator?
Enquote the null in the schema:
"type": ["string", "null"]
You can read about that in the json schema validation documentation, i.e.:
6.1. Validation Keywords for Any Instance Type
6.1.1. type
The value of this keyword MUST be either a string or an array. If it is an array, elements of the array MUST be strings and
MUST be unique.
String values MUST be one of the six primitive types ("null",
"boolean", "object", "array", "number", or "string"), or "integer"
which matches any number with a zero fractional part.
An instance validates if and only if the instance is in any of the
sets listed for this keyword.
The type attribute of the schema does not accept arrays but only a single type at the time:
"string", "null"... and as you pointed out, the types should be strings so instead of null => "null"
If you want to check multiple types for a single field you need to use
anyOf, oneOf, allOf
Here is an example working with your input
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product set",
"type": "array",
"items": {
"title": "Product",
"type": "object",
"properties": {
"name": {
"anyOf": [
{"type":"string"},
{"type":"null"},
{"type":"number"}
]
}
}
}
}