An alternate title for this question would be "required property combinations".
Say I am working with a json-schema like so:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "JSON schema for NLU (npm-link-up) library.",
"type": "object",
"additionalProperties": false,
"required": [
"list", "packages", "deps"
],
// ...
}
what I want to do, is make one of "list", "packages", "deps", to be required. That is one, but no more than one, should be present.
So it might be something like:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "JSON schema for NLU (npm-link-up) library.",
"type": "object",
"additionalProperties": false,
"required": [
{
"min": 1,
"max": 1,
"selection": ["list", "packages", "deps"]
}
],
}
or
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "JSON schema for NLU (npm-link-up) library.",
"type": "object",
"additionalProperties": false,
"required": [
{
"operator": "or",
"selection": ["list", "packages", "deps"]
}
],
}
is this possible?
There are four boolean combinator keywords in JSON Schema:
allOf - AND
anyOf - OR
oneOf - XOR (eXclusive OR)
not - NOT
What you want can be done like this ...
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "JSON schema for NLU (npm-link-up) library.",
"type": "object",
"additionalProperties": false,
"oneOf": [
{ "required": ["list"] },
{ "required": ["packages"] },
{ "required": ["deps"] }
]
}
Related
I am trying to get proper validation error from oneof or anyof pattern. I have json schema with two or more oneof/anyof condition as mentioned below:
json_schema = {
"type": "object",
"properties": {
"comment": {
"description": "Server Pool Policy Qualification Comments",
"type": "string",
"default": ""
},
"name": {
"description": "Server Pool Policy Qualification Name",
"type": "string",
"default": "",
"pattern": "^[\\-\\.:_a-zA-Z0-9]{1,16}$"
},
"qualifications": {
"description": "Qualifications of Server Pool Policy Qualification",
"type": "array",
"items": {
"description": "Qualification of Server Pool Policy Qualification",
"type": "object",
"oneOf": [
{
"properties": {
"type": {
"description": "Qualification Type",
"type": "string",
"enum": [
"adapter"
]
},
"adapter_qualification":{
"description": "Adapter Qualifications - Adapter Type",
"type": "array",
"properties": {
"adapter_type": {
"description": "Adapter Qualifications - Adapter Type",
"type": "string",
"enum": [
"virtualized-scsi-if"
]
},
"adapter_pid": {
"description": "Adapter Qualifications - Adapter PID (RegEx)",
"type": "string",
"default": "",
"pattern": "[ !#$%\\(\\)\\*\\+,\\-\\./:;\\?#\\[\\\\\\]\\^_\\{\\|\\}~a-zA-Z0-9]{0,256}"
},
"adapter_maximum_capacity": {
"description": "Adapter Qualifications - Maximum Capacity",
"type": "string",
"default": "unspecified",
"pattern": "^unspecified$|^[0-9]$|^[0-9][0-9]$|^[0-9][0-9][0-9]$|^[0-9][0-9][0-9][0-9]$|^[0-5][0-9][0-9][0-9][0-9]$|^6[0-4][0-9][0-9][0-9]$|^65[0-4][0-9][0-9]$|^655[0-2][0-9]$|^6553[0-5]$"
}
},
"additionalProperties": False,
"required": [
"type",
"adapter_type"
]
}
}
},
{
"properties": {
"type": {
"description": "Qualification Type",
"type": "string",
"enum": [
"server_pid"
]
},
"server_pid": {
"description": "Server PID Qualifications - Server PID",
"type": "string",
"default": "",
"pattern": "^123$"
}
},
"additionalProperties": False,
"required": [
"type",
"server_pid"
]
}
]
}
}
},
"additionalProperties": False,
"required": [
"name"
]
}
I have data which has additional element first_rack_id but best matches 2nd element from oneof.
data = {
"descr": "description",
"name": "domainGroup",
"qualifications": [
{
"server_pid": "B200M5",
"type": "server_pid",
"first_rack_id": "10"
}
]
}
validator = Draft7Validator(json_schema)
best = best_match(validator.iter_errors(data))
My expectation is that the error message thrown by validation will find 2nd element from oneof and throw error saying additional property is not allowed. but i get match for 1st element as mentioned below:
'server_pid' is not one of ['adapter']
Failed validating 'enum' in schema[0]['properties']['type']:
{'description': 'Qualification Type',
'enum': ['adapter'],
'type': 'string'}
On instance['type']:
'server_pid'
how do i specify validator to best match with property "type" which will match with enum "server_pid" instead of enum "adapter"
You can specify which schema to validate against with the if/then keywords. It's a bit verbose and can be error prone, but it's the best way to express this sort of thing. Although popular, oneOf is almost never the right choice.
"allOf": [
{
"if": {
"type": "object",
"properties": {
"type": { "const": "adapter" }
},
"required": ["type"]
},
"then": { "$ref": "#/definitions/adapter" }
},
{
"if": {
"type": "object",
"properties": {
"type": { "const": "server_pid" }
},
"required": ["type"]
},
"then": { "$ref": "#/definitions/server-pid" }
}
],
Since best_match need a sort key to help match errors and the default key is to use most depth errors key, see:
best_match
relevance
So maybe you can use a less depth key to match the errors.(below function is just a draft test, you can use it as reference)
def match_less_path(error):
return len(error.path)
best = best_match(validator.iter_errors(data), match_less_path)
And I test the output like this:
Additional properties are not allowed ('first_rack_id' was unexpected)
Failed validating 'additionalProperties' in schema[1]:
{'additionalProperties': False,
'properties': {'server_pid': {'default': '',
'description': 'Server PID '
'Qualifications - Server '
'PID',
'pattern': '^123$',
'type': 'string'},
'type': {'description': 'Qualification Type',
'enum': ['server_pid'],
'type': 'string'}},
'required': ['type', 'server_pid']}
On instance:
{'first_rack_id': '10', 'server_pid': 'B200M5', 'type': 'server_pid'}
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" ]
}
}
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
}
I've got a json schema and I have 3 types of media, caption, image, and avatar.
Each of these media types has a different structure, so I'm using $ref and oneOf to specify which are valid options.
However, I can't figure out how to specify which ref to use based on a the value of a sibling.
My schema looks like this
const mediaSchema = {
"type": "object",
"required": ["mediaType", "content", "points"],
"properties":{
"mediaType": {"type":"string", "pattern": "^(image|avatar|caption)$"},
"content": {
"oneOf": [
{"$ref":"#/definitions/image"},
{"$ref": "#/definitions/caption"},
{"$ref": "#/definitions/avatar"}
],
}
},
"definitions": {
"caption":
{"type": "object",
"required": ["text"],
"properties": {
"text": {"type": "string"},
"fontSize": {"type": "string", "pattern": "^[0-9]{1,3}px$"}
}
},
"image": {"type": "string", "format": "url"},
"avatar":
{"type": "object",
"properties": {
"name": {"type": "string"},
"image": {"type": "string", "format":"url"}
}
}
}
}
and when I define an avatar like
mediaItem = {
"mediaType":"avatar",
"content": {
"name": "user name",
"avatar": "https://urlToImage
}
}
it should be valid, but if I define an avatar as
mediaItem = {
"mediaType": "avatar",
"content": "https://urlToImage"
}
it should throw an error as that is not valid for a media type of avatar.
You are on the right track, but you should put the oneOf dispatcher to the root of the schema, and define the "content" with 3 separate constants as a discriminator, like this:
{
"oneOf": [
{
"type": "object",
"properties": {
"mediaType": {
"const": "avatar"
},
"content": { "$ref": "#/definitions/avatar" }
},
"required": ["mediaType", "content"]
},
// ...
],
"definitions": {
// ...
}
}
Note: the "const" keyword exists only in the latest version of json schema (draft6). It may happen that the validator implementation you use doesn't support it yet. In that case you can replace "const": "avatar" with a single-element enum like "enum": ["avatar"]
Facing an issue with schema validation.
schema :
{
"type": "object",
"$schema": "http://json-schema.org/draft-03/schema",
"id": "#",
"required": true,
"patternProperties": {
"^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,6}$": {
"type": "object",
"required": true,
"properties": {
"_from": {
"id": "_from",
"type": "string",
"required": true
},
"message": {
"type": "object",
"id": "message",
"properties": {
"detail": {
"type": "string",
"id": "detail",
"required": true
},
"from": {
"type": "string",
"id": "from",
"required": true
}
}
}
}
}
}
}
json :
{
"tom#example.com": {
"_from": "giles#gmail.com",
"message": {
"from": "Giles#gmail.com",
"detail": "AnyonewanttomeetmeinParis"
}
},
"harry#example.com": {
"_from": "giles#gmail.com",
"message": {
"from": "Giles#gmail.com",
"detail": "AnyonewanttomeetmeinParis"
}
}
}
Here the key email address is dynamic, somehow it doesn't validate regex for email validation.
Can you please advise me to correct the schema.
I am validating using : http://json-schema-validator.herokuapp.com/index.jsp
I see in your pattern that you seem to have forgotten to escape some characters or didn't do it correctly:
"^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,6}$"
and it causes the error that you can see when you hover the mouse over the link at the top of the validator:
it should be:
"^[A-Z0-9\\._%\\+-]+#[A-Z0-9\\.-]+\\.[A-Z]{2,6}$"
or without escaping the inner/class characters but I'd use the first pattern because I think its intention is clearer:
"^[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,6}$"
You need to have two \ because the first \ is an escape for the second \. With a single one it wouldn't work because there is no escape sequence like \. or \+ in javascript. You want to have a \in the pattern itself.
However json schema patternProperties are case sensitive by default so you need to extend your email pattern by adding a-z to it:
"^[A-Za-z0-9\\._%\\+-]+#[A-Za-z0-9\\.-]+\\.[A-Za-z]{2,6}$"
(I didn't find any other way to make it case insensitive)
You also need to exclude any other property names by adding "additionalProperties": false next to the patternProperties or otherwise it catches everything else that does not match the pattern.
The working schema should then look like this:
{
"type": "object",
"$schema": "http://json-schema.org/draft-03/schema",
"id": "#",
"required": true,
"patternProperties": {
"^[A-Za-z0-9\\._%\\+-]+#[A-Za-z0-9\\.-]+\\.[A-Za-z]{2,6}$": {
"type": "object",
"required": true,
"properties": {
"_from": {
"id": "_from",
"type": "string",
"required": true
},
"message": {
"type": "object",
"id": "message",
"properties": {
"detail": {
"type": "string",
"id": "detail",
"required": true
},
"from": {
"type": "string",
"id": "from",
"required": true
}
}
}
}
}
},
"additionalProperties": false
}
I've tested it on: http://jsonschemalint.com/
Changed the schema as per draft 04 :
{
"type": "object",
"$schema": "http://json-schema.org/draft-04/schema",
"patternProperties": {
"^[A-Za-z0-9\\._%\\+-]+#[A-Za-z0-9\\.-]+\\.[A-Za-z]{2,6}$": {
"type": "object",
"properties": {
"__from": {
"type": "string"
},
"message": {
"type": "object",
"properties": {
"from": {
"type": "string"
},
"detail": {
"type": "string"
}
},
"required": [ "from","detail"]
}
},
"required": [ "__from","message"]
}
},
"additionalProperties": false
}