JSON Schema v4 "required" in nested object - json

I tried searching, but I'm not quite sure how to put this in words! The point of confusion is how "required" works in JSON schema v4 when there are nested key values with the same name.
For example, this schema:
{
"Root": {
"type": ["array", "null"],
"items": {
"type": "object",
"properties": {
"LevelOne": {
"required": ["id", "name", "LevelOneRepeat"],
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"LevelOneRepeat": {
"type": ["array", "null"],
"items": {
"type": "object",
"properties": {
"required": ["id", "name"],
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
}
}
}
}
}
}
Inside LevelOne, I have a required for "id", "name", and "LevelOneRepeat". However, inside LevelOneRepeat, I also have a required for "id" and "name".
Does required only check for elements in the same level, or also all child elements? Do I need to include the required inside LevelOneRepeat if the key values required (same name) are already listed in the level above?
I tested my JSON with my schema, but I might've messed up my code somewhere as no required are working anymore.

You have a couple of issues with your schema that is probably what has led to your confusion about how required works.
Here is the corrected schema.
{
"type": ["array", "null"],
"items": {
"type": "object",
"properties": {
"LevelOne": {
"type": "object",
"properties": {
"id": { "type": "string" },
"name": { "type": "string" },
"LevelOneRepeat": {
"type": ["array", "null"],
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"name": { "type": "string" }
},
"required": ["id", "name"]
}
}
},
"required": ["id", "name", "LevelOneRepeat"],
}
}
}
}
The first problem is with the way you define nested objects. The value of each property must be a schema. See how I changed the LevelOne definition to see how to correctly define a nested object.
The second problem is that have the required keyword in the wrong place. It should be on the same level as the properties keyword, not nested within the properties object. This is why your required constraints weren't working. See how I changed the LevelOne and LevelOneRepeat definitions.
Once you fix these problems, hopefully it should be more clear that the required keyword only applies to the schema it is defined in. It does not apply to any parent or child schema.

The id in LevelOne and LevelOneRepeat are only coincidentally related. They have to be independently specified as required.

Related

How to write a valid JSON Schema for a document with only optional elements

I use JSON Schema to validate YAML and JSON configuration files. In one configuration file are all elements optional. But only is a limited set of elements is allowed.
What do I have to change in the given schema below, so that an empty file/document in case of JSON is also valid?
{
"$id": "https://snafu.com/api/service/publishing-mechanism/config-delta/1.0.0",
"$schema": "https://json-schema.org/draft/2019-09/schema",
"description": "....",
"type": "object",
"additionalProperties": false,
"properties": {
"plans": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"patternProperties": {
"^.*$": {
"type": "object",
"additionalProperties": false,
"properties": {
"description": {
"type": "string"
},
"validation": {
"type": "string",
"enum": ["auto", "manual"]
},
"security": {
"type": "string",
"enum": ["api_key"]
}
}
}
}
}
}
}
You can remove your patternProperties and just collapse that into additionalProperties -- which takes a schema, not just true/false. Because the patternProperties is a wildcard that matches everything, the additionalProperties there adds no value.
Otherwise, aside from the "minProperties": 1 which states that there cannot be zero properties, your definition already allows for optional properties at the top level, and at the "plans" level, all properties are optional as well. Use the required keyword to ensure that a property is actually present:
Reference: https://json-schema.org/understanding-json-schema/reference/object.html
So, after quite some time, here is the solution which is working for my project in production.
{
"$id": "https://snafu.com/api/service/publishing-mechanism/config-delta/1.0.0",
"$schema": "https://json-schema.org/draft/2019-09/schema",
"description": "....",
"$defs": {
"empty": {"type": "null"}
},
"type": "object",
"additionalProperties": false,
"properties": {
"plans": {
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^.+$": {
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"properties": {
"description": {
"minLength": 1,
"type": "string"
},
"security": {
"enum": ["api_key"],
"type": "string"
}
}
},
{ "$ref": "#/$defs/empty" }
]
}
}
},
{ "$ref": "#/$defs/empty" }
]
}
}
}
Here I use oneOf to have two mutually exclusive subschemas. One of the is all the possible properties, where one of them is required.
The second subschema has null as type. Therefore the only acceptable value for this schema is null.

JSON Schema reference resolution

I have a JSON schema that contains "$ref" tags and I am trying to get a version of the JSON schema that have the "$ref" tags resolved. I am only looking to resolve "$ref" from definition (tags) within the JSON Schema string (ie. not external resolution needed).
Is there a library that performs the resolution of the JSON Schema? (I am currently using org.everit.json.schema library, which is great, but I can't find how to do what I need).
For example, my original schema is:
{
"$id": "https://example.com/arrays.schema.json",
"description": "A representation of a person, company, organization, or place",
"title": "complex-schema",
"type": "object",
"properties": {
"fruits": {
"type": "array",
"items": {
"type": "string"
}
},
"vegetables": {
"type": "array",
"items": { "$ref": "#/$defs/veggie" }
}
},
"$defs": {
"veggie": {
"type": "object",
"required": [ "veggieName", "veggieLike" ],
"properties": {
"veggieName": {
"type": "string",
"description": "The name of the vegetable."
},
"veggieLike": {
"type": "boolean",
"description": "Do I like this vegetable?"
}
}
}
}
}
Which would resolve to something like this (notice that the "#defs/veggie" resolves to its definition inserted inline in the schema):
{
"$id": "https://example.com/arrays.schema.json",
"description": "A representation of a person, company, organization, or place",
"title": "complex-schema",
"type": "object",
"properties": {
"fruits": {
"type": "array",
"items": {
"type": "string"
}
},
"vegetables": {
"type": "array",
"items": {
"type": "object",
"required": [ "veggieName", "veggieLike" ],
"properties": {
"veggieName": {
"type": "string",
"description": "The name of the vegetable."
},
"veggieLike": {
"type": "boolean",
"description": "Do I like this vegetable?"
}
}
}
}
}
}
This isn't possible in the general sense, because:
the $ref might be recursive (i.e. reference itself again)
the keywords in the $ref might duplicate some of the keywords in the containing schema, which would cause some logic to be overwritten.
Why do you need to alter the schema in this way? Generally, a JSON Schema implementation will resolve the $refs automatically while evaluating the schema against provided data.

How do I inform a Json schema that it must have atleast one type of object in its Array, but other types are optional

I am working on updating a JSON schema for work.
For the json array, we have
"accountsInfo": [{
"type":"ADMIN",
"firstName":"Bill",
"lastName":"Cipher",
"emailAddress":"bcipher#gfalls.com"
}, {
"type":"USER"
"firstName":"Bugs",
"lastName":"Bunny",
"emailAddress":"whats#updoc.org"
}]
The USER type is needs to be optional for this schema, with the atleast 1 ADMIN type is required in the array. How can I do this?
Here is the portion of the schema file. It is using Json Schema 7.
"accountsInfo": {
"type": "array",
"uniqueItems": true,
"minItems": 2,
"items": [
{
"type": "object",
"required": [
"type",
"firstName",
"lastName",
"emailAddress"
],
"properties": {
"type": {
"type": "string",
"enum": [
"ADMIN",
"USER"
]
},
"firstName": {
"type": "string",
"$ref": "#/definitions/non-empty-string"
},
"lastName": {
"type": "string",
"$ref": "#/definitions/non-empty-string"
},
"emailAddress": {
"type": "string",
"format": "email"
}
}
}
]
}
You can use the "contains" keyword for this. In pseudocode: "the array must contain (at least one) item that successfully evaluates against this schema".
As a sibling keyword to "type": "object" and "items": { ... }, add:
"contains": {
"properties": {
"type": {
"const": "ADMIN"
}
}
}
Also, you have an error in your "items" keyword: if you intend for that subschema to match all items, not just the first, remove the extra array around the schema. The array form of "items" matches each item in the data against each item in the schema in turn, and you only specify a schema for the first item, so all items after the first can be anything.
"items": { .. schema .. } not "items": [ { .. schema .. } ].
If using the contains keyword as suggested, and if you are using strict mode, you may need to add "type": "array" like this:
{
"type": "array",
"contains": {
"properties": {
"type": {
"const": "ADMIN"
}
}
}
}

JSON schema validation with Map of string with Enum constraints

Requesting help with JSON Schema validation, below is sample JSON and Schema. I am trying to figure out how to specify "ppd" schema rule specifically "cfg" is a map of String, String and need to further restrict the entries of the key and value in this map by Enum definition i.e. allowed values for "inputDateTimeFormat" is a valid date time format so rule should encode if key is "inputDateTimeFormat" then allowed value is a pattern matching date time format and similarly if key is "valuemapping" then allowed values is pattern matching k=v (example below).
Could you please suggest a way to achieve this?
JSON Sample -
{
"sm": [
{
"mid": "id-1",
"ppd": [
{
"name": "cc-1",
"cfg": {
"columns": "v-1",
"valueMapping": "B=01;S=02"
}
},
{
"name": "cc-2",
"cfg": {
"columns": "v-2",
"inputDateTimeFormat": "ddMMMyyyy_HH:mm:ss.SSSSSS",
"outputDateTimeFormat": "yyyy-MM-dd'T'HH:mm:Ss.SSSZ"
}
},
{
"name": "cc-3",
"cfg": {
"columns": "v-3;v-4",
"markers": "d=01"
}
}
]
}
]
}
JSON Schema :
{
"type": "object",
"$schema": "http://json-schema.org/draft-06/schema",
"id": "source-mappings-schema",
"required": true,
"properties": {
"sm": {
"type": "array",
"id": "source-mappings-schema/sm",
"required": true,
"items": {
"type": "object",
"id": "source-mappings-schema/sm/0",
"required": true,
"properties": {
"mappingId": {
"type": "string",
"id": "source-mappings-schema/sm/0/mappingId",
"required": true
},
"ppd": {
"type": "array",
"id": "source-mappings-schema/sm/0/ppd",
"required": true,
"items": {
"type": "object",
"id": "source-mappings-schema/sm/0/ppd/0",
"required": true,
"properties": {
"name": {
"type": "string",
"id": "source-mappings-schema/sm/0/ppd/0/name",
"required": true
},
"cfg": {
"type": "array",
"id": "source-mappings-schema/sm/0/ppd/0/cfg",
"required": true,
"items": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
To start with your schema contains a few issue.
The $schema tag is wrong, it should be
"$schema": "http://json-schema.org/draft-06/schema#",
The 'required' property is supposed to be an array of property names that are required (not a bool), so you need to apply this at the level above.
Finally the validation of cfg. By specifying a schema for 'additionalProperties' you can provide validation rules for all unspecified key values (you said it was a map of strings, so I've set it to string, but you could also add other rules here like max length etc).
For the keys you know about you can add a property for each of them with the approrate validation rules (the rules i've added demonstrate the concept and will need tweaking for your use).
"cfg": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"properties": {
"inputDateTimeFormat": {
"type": "string",
"format": "date-time"
},
"valuemapping": {
"type": "string",
"pattern": "[a-z]\\=[a-z]"
}
}
}

Json schema not validating required attribute

i'm coding the following Json Schema:
{
"$schema": "http://json-schema.org/schema#",
"title": "Layout",
"description": "The layout created by the user",
"type": "object",
"definitions": {
"stdAttribute": {
"type": "object",
"properties": {
"attributeValue": {
"type": "object"
},
"attributeName": {
"type": "string"
}
}
},
"stdItem": {
"type": "object",
"required" : ["stdAttributes"],
"properties": {
"stdType": {
"enum": [
"CONTAINER",
"TEXT",
"TEXTAREA",
"BUTTON",
"LABEL",
"IMAGE",
"MARCIMAGE",
"DATA",
"SELECT",
"TABLE"
]
},
"stdAttributes": {
"type": "array",
"items": {
"$ref": "#/definitions/stdAttribute"
},
"minItems": 1
},
"children": {
"type": "array",
"items": {
"$ref": "#/definitions/stdItem"
}
}
}
}
}
}
When i set the following data:
{
"stdItem": {
"stdType": "CONTAINER",
"stdAttributes": [],
"children": []
}
}
the validator says there is no error but in the schema i'm using a minItems and a ref to the "StdAttribute" schema in "StdAttributtes".
I tried to define this property in the base schema but the validator says the same thing.
How should i validate the type and number of items in "StdAttributes"?
I'm using the Java Validator.
You are missing a properties attribute at the top level. Right now the only thing your schema is validating is that your data is an object. definitions doesn't validate anything by itself. It's just a place to hold schemas that can be referenced in your schema. The following would be the minimum you would have to add to the root of your schema to get the results you expect.
"properties": {
"stdItem": { "$ref": "#/definitions/stdItem" }
}