JSON Schema enum does not affect validation - json

I have a sub-schema defined in nested objects and cannot make the enum constraint work. See here....
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"Top level": {
"type": "object",
"properties": {
"State": {
"type": "object",
"description": "stuff",
"properties": {
"Value": {
"type": "string",
"enum:": [
"A",
"B",
"C"
]
},
"readOnly": true
},
"required": [
"Value"
]
}
},
"required": [
"State"
]
}
},
"required": [
"Top level"
]
}
This should fail but instead it validates. Below...
{
"Top level": {
"State": {
"Value": "not supposed to validate but does anyway"
}
}
}
Oddly, this schema appears to work and block the undesired strings but it does not have the deeper sub-schema structure...
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"Value": {
"type": "string",
"enum": [
"A",
"B",
"C"
]
}
}
}
and this example properly gets rejected...
{
"Value": "D"
}
What am I doing wrong ? It must be something fundamental about nested objects.I know if I change the Value name, it detects it is missing and rejects during validation in the first example... why does it not detect the invalid enum strings ?
Any help would be appreciated !

This was really hard to spot for some reason. I thought I was going nuts too. You've got an extra : in there.
"enum:": [
^

Related

JSON Schema validation for a complex comination of AND/OR

Similar to this question. But it's a little more complicated because I need to validate fields in a list of objects.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "All the Fruits",
"type": "object",
"properties":
{
"fruit":
{
"enum":
[
"apple",
"orange",
"banana",
"watermelon",
"all_fruit"
]
}
},
"required":
[
"fruit"
],
"additionalProperties": false
}
And it's used here in a different file:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Fruit basket",
"type": "object",
"properties":
{
"type":
{
"enum":
[
"fruit_basket"
]
},
"fruits":
{
"description": "List of fruits",
"type": "array",
"items":
{
"$ref": "fruit/fruits.schema.json#"
},
"uniqueItems": true,
{
// What do I add here?
}
}
"required":
[
"fruits"
]
"additionalProperties": false
}
Is there a way to validate JSON such that I can pick any subset of fruits or "all_fruit" not both.
Valid cases:
["apple","orange"]
["apple","banana"]
["all_fruit"]
Invalid:
["all_fruit", "apple"]
all_fruit cannot exist with anything else.
Edit:
I tried this:
...
...
"uniqueItems": true,
"if" : {
"properties": {
"fruits": {
"const": "all_fruit"
}
}
},
"then": {
"maxItems": 1,
"minItems": 1
}
...
...
But the condition seems to be satisfied always. Even when I don't use all_fruit and the validation errors out saying maxItems expected is 1.
See https://json-schema.org/understanding-json-schema/reference/conditionals.html and https://json-schema.org/understanding-json-schema/reference/array.html
-- in pseudocode, you'd want:
if the list is exactly the value of ["all_fruit"],
then maximum items is 1.
If "all_fruit" could coexist with some values but not all, you could instead use contains instead of const for the conditional.

How to perform length check validation under certain conditions using Json Schema Validator?

I have json schema structure that look like below.
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"description": "My sample Json",
"type": "object",
"properties": {
"eventId": {
"description": "The event Indetifier",
"type": [ "number", "string" ]
},
"serviceType": {
"description": "The service type. It can be either ABC or EFG",
"enum": [ "ABC", "EFG" ]
},
"parameters": { "$ref": "/schemas/parameters" }
},
"required": [ "eventId", "serviceType" ],
"$defs": {
"parameters": {
"$id": "/schemas/parameters",
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Other Parameters",
"type": "object",
"properties": {
"activityType": {
"description": "The activity type",
"type": [ "null", "string" ]
},
"activitySubType": {
"description": "The activity sub type",
"type": [ "null", "string" ]
}
}
}
}
}
Now I have requirement to perform some validation logic.
If eventId == "100" and serviceType == "ABC" then parameters.activityType should be not null and must have a minimum length of 10.
If eventId == "200" and serviceType == "EFG" then parameters.activitySubType should be not null and must have a minimum length of 20.
I am trying to perform the validation using "if then else" condition. I am not sure how to add that inside the Json Schema validator.
Can anyone help me with the syntax? Is it possible to do that?
This is definitely possible. For the first requirement:
{
...
"if": {
"required": [ "eventId", "serviceType" ],
"properties": {
"eventId": {
"const": "100"
},
"serviceType": {
"const": "ABC"
}
}
},
"then": {
"required": [ "parameters" ],
"properties": {
"parameters": {
"properties": {
"activityType": {
"type": "string",
"minLength": 10
}
}
}
},
...
}
The "required" keywords are there because if the property doesn't exist at all, the "if" subschema will validate as true, which you don't want -- you need to say "if this property exists, and its value is ..., then ...".
The second requirement is very similar to the first. You can use multiple "if"/"else" keywords in the schema at the same time by wrapping them in an allOf: "allOf": [ { ..schema 1..}, { ..schema 2.. } ]

JsonSchema - Only allow one item in an array field if another array field is defined

I have the following json and would like to only allow "leads" to contain only a single "id" item if there is a "tokens" array present (with at least one item).
JSON
{
"input": {
"leads": [
{
"id": 795333333760
}
],
"tokens": [
{
"name": "tem_x",
"value": "Renew_all"
},
{
"duration": "90",
"eligibility": "eligible"
}
]
}
I have the following schema, that indicates "tokens" can have more than one item if present and that "leads" is required.
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"input": {
"type": "object",
"properties": {
"leads": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"id": {
"type": "integer"
}
},
"required": [
"id"
]
}
]
},
"tokens": {
"type": "array",
"minItems": 1
}
},
"required": [
"leads",
]
}
},
"required": [
"input"
]
}
Is there a way to ensure that only one item in "leads" can be allowed if "tokens" is present (as it is not defined as a required field). If "tokens" is not defined, then I would like to allow the "leads" array to have more than one item.
I played around with if-then but wasn't able to get it working right. Any help is appreciated.
Thank you.
This is the kind of thing the dependencies keyword is for. It can be done with if/then or implication as well, but dependencies removes all the extra boilerplate needed for those patterns. The following says, if the "tokens" property is defined, then the "leads" property must have at most 1 item. This would go inside your "input" schema.
"dependencies": {
"tokens": {
"properties": {
"leads": { "maxItems": 1 }
}
}
}
Edit: dependencies works in draft-04 thru draft-07. In draft 2019-09 and up, you can use dependentSchemas instead.
try to use like this:
{
"properties": {
"tokens": {},
"leads": {}
},
"anyOf": [{
"required" : ["tokens"]
}, {
"required" : ["leads"]
}]
}
so schema looks like this:
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"input": {
"type": "object",
"properties": {
"leads": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"id": {
"type": "integer"
}
},
"required": [
"id"
]
}
]
},
"tokens": {
"type": "array",
"minItems": 1
}
},
"anyOf": [{
"required" : ["tokens"]
}, {
"required" : ["leads"]
}]
}
},
"required": [
"input"
]
}

Conditionally Merging JSON Schema Properties

I'm trying to create a JSON schema to validate YAML for some VSCode intellisense. What I'm trying to do is choose the correct subschema to use for a property in the main schema based on an adjacent key's value.
Some JSON examples:
[
{
"name": "doesntmatter",
"matchMe": "stringToMatch:123whatever",
"mergeMe": {
"key1": "value1",
"key2": "value2"
}
}
]
[
{
"name": "doesntmatter",
"matchMe": "anotherStringToMatch:123whatever",
"mergeMe": {
"anotherKey": "valueSomething",
"anotherKey2": "cheese"
}
}
]
So I need to choose the correct schemas for the mergeMe objects based on the substring match of matchMe. After following a bunch of answers, I'm at a point where I can either make it match multiple, and error my linter, or match none, but an online validator says it's ok (except nothing matches as the required fields aren't triggering).
I moved my sub-schemas to be merged into definitions to reference them, and then used an if/then to match. That worked with one, but then I tried to expand it to do the tree matching, and I can't get that to work. Someone said that I should wrap my if/thens in an allOf (I'm not sure why that would work since surely not all of them would match?). Changing it to an anyOf makes none of them match and I get no intellisense. Nor do I really understand why I should wrap single if/thens or thens in allOfs.
The idea is that based on the pattern it uses a definitions schema to match the mergeMe property, but the conditional logic isn't quite right. Thinned schema below:
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "http://example.com/example.json",
"type": "array",
"title": "The root schema",
"description": "The root schema comprises the entire JSON document.",
"default": [],
"additionalItems": true,
"definitions": {
"stringToMatch": {
"$id": "#/definitions/stringToMatch",
"type": "object",
"properties": {
"key1": {
"type": "string"
}
},
"required": [
"key1"
],
"additionalProperties": true
},
"anotherStringToMatch": {
"$id": "#/definitions/anotherStringToMatch",
"type": "object",
"properties": {
"key2": {
"type": "string"
}
},
"required": [
"key2"
],
"additionalProperties": true
}
},
"items": {
"$id": "#/items",
"type": "object",
"title": "main schema",
"description": "An explanation about the purpose of this instance.",
"default": {},
"examples": [],
"required": [
"name",
"matchMe",
"mergeMe"
],
"properties": {
"name": {
"$id": "#/items/name",
"type": "string",
"title": "The name schema",
"description": "An explanation about the purpose of this instance.",
"default": "",
"examples": []
},
"matchMe": {
"$id": "#/items/matchMe",
"type": "string",
"title": "The matchMe schema",
"description": "An explanation about the purpose of this instance.",
"default": "",
"examples": []
}
},
"allOf": [
{
"if": {
"properties": {
"matchMe": {
"pattern": "^stringToMatch:[0-9.]+"
}
}
},
"then": {
"allOf": [
{
"type": "object",
"properties": {
"mergeMe": {
"$ref": "#/definitions/stringToMatch"
}
}
}
]
}
},
{
"if": {
"properties": {
"gear": {
"pattern": "^anotherStringToMatch:[0-9.]+"
}
}
},
"then": {
"allOf": [
{
"type": "object",
"properties": {
"mergeMe": {
"$ref": "#/definitions/anotherStringToMatch"
}
}
}
]
}
}
],
"additionalProperties": true
}
}
What I want in JS would look something like
const schema = { name, matchMe }
if (matchMe == "string1") schema.mergeMe = ...subschema1;
else if (...)
else if (...)
but I just can't really work it out. Can someone help?
Edit: jsonschema.dev playground - the idea being if I specify the food as prefixed by "fruit" I have to give it "pips" and "berry", whereas if I specify "vegetable" I have to give it a totally differet schema, and they don't overlap.
https://jsonschema.dev/s/pHzGo
This actually ended up being a bug in the VSCode YAML extension that was ingesting my schema, causing the if blocks to not evaluate, and has been raised, fixed and released.

JSON Schema Nested If Then

I cannot seem to find a working way of applying multiple if/then logic on an enum.
anyOf doesnt apply the conditional logic, but instead it says if any of them match then thats good.
allOf again doesnt apply the conditional logic but tests a superset of the properties/required fields.
Here is a JSON Schema example:
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://example.com/root.json",
"type": "object",
"title": "The Root Schema",
"required": [
"type"
],
"properties": {
"type": {
"$id": "#/properties/type",
"enum": [
"a",
"b",
"c"
],
"title": "The Type"
},
"options": {
"$id": "#/properties/options",
"type": "object",
"title": "The Options Schema",
"oneOf": [
{
"if": { "properties": { "type": { "const": "a" } }
},
"then": {
"required": [ "option1" ],
"properties": {
"option1": {
"$id": "#/properties/options/properties/option1",
"type": "boolean",
"title": "The option1 Schema"
}
}
}
},
{
"if": { "properties": { "type": { "const": "b" } }
},
"then": {
"required": [ "option2" ],
"properties": {
"option2": {
"$id": "#/properties/options/properties/option2",
"type": "boolean",
"title": "The option2 Schema"
}
}
}
},
{
"if": { "properties": { "type": { "const": "c" } }
},
"then": {
"required": [],
"properties": {}
}
}
]
}
}
}
If you validate against this JSON:
{
"type": "a",
"options": {
"option1": true
}
}
It fails because option2 is required.
If you change it to anyOf then it succeeds, but if you change the JSON to be invalid:
{
"type": "a",
"options": {
"option2": false
}
}
It still succeeds.
I havent managed to get nested if/then/else/if/then/else working either.
How can i perform a check where I have set of properties for each type and you cannot intermingle them? Is this actually possible, or should I just change my design.
First, you can test your schemas here. There are several of these sites across the internet.
Second, the if/then/else construct was introduced to replace a oneOf for these kind of enum scenarios, not be combined with it.
This subschema
"if": { "properties": { "type": { "const": "a" } } },
"then": {
"required": [ "option1" ],
"properties": {
"option1": {
"$id": "#/properties/options/properties/option1",
"type": "boolean",
"title": "The option1 Schema"
}
}
}
doesn't actually fail when type is not a. It merely says that if type=a, apply the then subschema. It doesn't say anything about what to validate if type is not a, so it passes. If you add an else:false to this, it'll be more in line with what you're thinking, but I encourage you to think about it differently.
Use oneOf or if/then/else, but not both. I suggest changing your subschemas to use this format:
{
"properties": {
"type": { "const": "a" },
"option1": {
"$id": "#/properties/options/properties/option1",
"type": "boolean",
"title": "The option1 Schema"
}
},
"required": [ "option1" ],
}
This asserts that option1 is required and must be a boolean, and that type=a. If type is not a, this schema fails, which is what you want.
This answer describes what you need to do in a bit more detail.