JSONSchema - Element can appear alone or within a group - json

I have a JSON that looks like this:
{
"name": "Jane",
"company_name": "BrandNewStartup",
"designation": "DesignLead"
}
The name element can appear alone in the JSON as well, so the following JSON is valid too.
{
"name": "Jane"
}
But company_name and designation cannot appear if name is missing. So the following JSON should be invalid:
{
"company_name": "BrandNewStartup",
"designation": "DesignLead"
}
I have tried the following rule:
"oneOf": [
{
"required": [
"name",
"company_name",
"designation"
]
},
{
"required": [
"name"
]
}
]
However this does not seem to work (e.g this validation library raises error that the JSON should be valid to only one schema but it is valid against all).
If I change this to anyOf, the first JSON with all the 3 fields works, but when name appears alone, an error is raised that the company_name designation fields are missing.
How do I define this rule?

JSON Schema is a constraints based language. Anything you don't specify is allowed.
The required keyword means a key is required in an object, but doesn't inherintly prevent any other keys from being included.
Breaking down the schema in your question, when you have all three keys in your object, as in your first example instance, then both subschemas in oneOf would be valid.
In order to restrict the allowed properties, you need to use the additionalProperties keyword, which in your case also means you need to use the properties keyword. required has no effect on additionalProperties.
The second subschema needs to only be valid when the instance has ONLY name and no other keys. Here's a live demo using the below modified JSON Schema: https://jsonschema.dev/s/7JcUa
{
"$schema": "http://json-schema.org/draft-07/schema",
"oneOf": [
{
"required": [
"name",
"company_name",
"designation"
]
},
{
"required": [
"name"
],
"properties": {
"name": true
},
"additionalProperties": false
}
]
}

Related

Is there a Json schema validation implementation give all the missing required fields?

Normally, when validate a complicated json object, if an embedded field is required but the parent which is also required is missing, a validator only give the result saying the parent is required.
Wondering if there is a way (an implementation of json schema validator) to find all the mandatory fields (in the leaves of a json object) by applying a json schema validation?
Using https://www.jsonschemavalidator.net/
With schema
{
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "object",
"required": [
"firstName",
"secondName"
],
"properties": {
"firstName": {
"type": "string"
},
"secondName": {
"type": "string"
}
}
}
}
}
To validate an empty json object {}.
Can only get one error Message:
Required properties are missing from object: name.
Schema path: #/required
Can a validator give all the required fields including first and second names back in the error message?

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 each object key in the object array with json schema in ajv

I have an array which includes objects which have different key values. I want to validate each object key. For example, age field can get only Equal and Not equal operator values. So "op" key is different for each key. For example name should be used with Contains operator.
[
{ age:21, op: "Equal" },
{ name:21, op: "Contains" },
{ date: 1564577662198, op: "Not equal" }
]
I have written a schema as,
{
"title": "ValidatorSchema",
"type": "array",
"items": {
"type": "object",
"properties": {
"age":{
"type": "number"
},
"operator" : {
"type" : "string",
"enum" : ["Equal" , "Not equal"]
},
"name":{
"type": "string"
},
"operator" : {
"type" : "string",
"enum" : ["Contains"]
}
}
}
}
But i couldn't relate each key with an operator. How can I do it?
Here's one way to express the requirements (as best I understand them) using the "JSON Extended Structural Schema" language, JESS:
[
["&",
{"comment": "https://stackoverflow.com/questions/57291339/validate-each-object-key-in-the-object-array-with-json-schema-in-ajv",
"::<=": {
"age": "number",
"operator": "string",
"name": "string",
"date": "number"
}
},
{ "comment": "age field can get only Equal and Not equal operator values",
"ifcond": { "has": "age"},
"then": ["&", {"forall": ".[operator]", "enumeration": ["Equal", "Not equal"]}]
},
{ "comment": "name should be used with Contains operator",
"ifcond": { "forall": ".[operator]", "equal": "Contains" },
"then": ["&", {"has": "name" } ]
}
]
]
Explanation
A JESS schema is a collection of JSON texts. Here, one JSON document suffices, as shown above.
The outer square brackets express the requirement that the target JSON entity must be a JSON array.
The "&" signifies that what follows is a conjunction of constraints. Here there are three top-level constraints, each with a "comment" field to indicate what's going on. In brief:
The first top-level constraint expresses the requirement that the components of the array must contain objects formed using any of the indicated keys with the type constraint shown.
The second top-level constraint expresses the condition that the "age field can get only Equal and Not equal operator values".
The third expresses the constraint that if the value of .operator is "Contains" in an object, then that object must have a "name" key (the type of which has already been specified in (1.)).

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.

How to validate string and number using json schema

I would like to validate a schema based on either its maximum/minimum (number) OR maximumLength/minimumLength (string).
I have a json form:
[
{
"key":"foo",
"title":"Test",
"type":"string"
}
]
and a json schema:
{
"type": "object",
"properties": {
"foo": {
"type": ["number","string"],
"maxLength":2,
"minLength":0,
"minimum":3,
"maximum":10
}
}
}
and a json model:
{
"foo": "bar"
}
Why does this example not work with validation? The model I have is not validated to false. According to this document it is possible to have different types defined in an array, but how can we do validation based on min/max values?
Your schema is correct. The validator you are using doesn't work properly. Here is an alternative that uses anyOf instead.
{
"type": "object",
"properties": {
"foo": {
"anyOf": [
{ "$ref": "#/definitions/boundedNumber" }
{ "$ref": "#/definitions/boundedString" }
]
}
},
"definitions": {
"boundedString": {
"type": "string",
"maxLength": 2,
"minLength": 0
},
"boundedNumber": {
"type": "number",
"minimum": 3,
"maximum": 10
}
}
}
Although it is quite a bit longer, some would argue that this is actually easier to read/maintain because of the separation of the type specific keywords.
Your schema is validating JSON objects ("type":"object"). In addition, if they have a property with key "foo", its value must be either a number between 3 an 10, or a string of maximum length 2.
Valid objects according to your schema:
{"foo":6}
{"foo":"as"}
Invalid objects:
{"foo":60}
{"foo":"asereje"}
If you want to validate arrays you must define your parent object as an array and use items tag to specify the schema for the array items, for instance:
{
"type" : "array",
"items" : {
"type" : "object",
"properties" : {
"foo" : {
"type" : ["number", "string"],
"maxLength" : 2,
"minLength" : 0,
"minimum" : 3,
"maximum" : 10
}
}
}
}
The schema above would validate the following JSON array:
[{
"foo" : 6
}, {
"foo" : "as"
}
]
John the issue is the type "type" : ["number", "string"]
Angular Schema Form is generating fields from the combined JSON Schema and the UI Schema (form), when the field type is defined it knows which type to validate against, when there are multiple types it doesn't know which part of the spec to base the form field against, so it falls back to a textbox and does not add the appropriate validation requirements for string alone.
To achieve your desired outcome you would need to tell it which field type you wish to use. There is a bug in the 0.8.x versions where it does not validate based on the type set in the UI schema if it cannot determine the type in the data schema, I believe that is fixed in the latest development branch. If not if there is an issue raised in Git, I would prioritise it.