Add pattern validation in json schema when property is present - json

Below is my schema definition and I would like to add pattern that depends on environment propertyName (env1, env2 or env3). Each env should have different pattern. For instance when env1 is present then url will have a different pattern than when env2 is present etc.
{
"environments": {
"env1": {
"defaultAccess": {
"url": [
"something-staging"
]
}
}
}
}
My current schema definition for that example
{
"$schema": "https://json-schema.org/draft-07/schema#",
"definitions": {
"envType": {
"type": "object",
"properties": {
"defaultAccess": {
"type": "object",
"properties": {
"url": {
"type": "string",
"pattern": "^[a-zA-Z0-9- \/]*$"
}
},
"required": [
"url"
]
}
}
},
"environmentTypes": {
"type": "object",
"properties": {
"env1": {
"$ref": "#/definitions/envType"
},
"env2": {
"$ref": "#/definitions/envType"
},
"env3": {
"$ref": "#/definitions/envType"
}
}
},
"type": "object",
"properties": {
"environments": {
"$ref": "#/definitions/environmentTypes"
}
}
}
}
In my head I have something like this but do not know how to apply it to the schema properly.
{
"if": {
"properties": {
"environments": {
"env1" : {}
}
}
},
"then":{
"properties": {
"environments-env1-defaultAccess-url" : { "pattern": "^((?!-env2).)*$" }
}
}
}
etc..

If understand correctly what you're trying to do, you shouldn't need conditionals for this kind of thing.
You have an error in your schema that might be tripping you up. You have your main schema inside the definitions keyword. If you run this through a validator, you should get an error saying that the value a /definitions/type must be an object.
Aside from that, schema composition using allOf should do the trick. Below, I've shown an example at /definitions/env1Type.
It looks like you were hoping for a less verbose way to specify a schema deep in an object structure (""). Unfortunately, there's no way around having to chain the properties keyword all the way down like I've demonstrated at /definitions/env1Type.
{
"$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"environments": { "$ref": "#/definitions/environmentTypes" }
},
"definitions": {
"environmentTypes": {
"type": "object",
"properties": {
"env1": { "$ref": "#/definitions/env1Type" },
"env2": { "$ref": "#/definitions/env2Type" },
"env3": { "$ref": "#/definitions/env3Type" }
}
},
"envType": { ... },
"env1Type": {
"allOf": [{ "$ref": "#/definitions/envType" }],
"properties": {
"defaultAccess": {
"properties": {
"url": { "pattern": "^((?!-env1).)*$" }
}
}
}
},
"env2Type": { ... },
"env3Type": { ... }
}
}

Related

How do I reference properties in Json Schema?

I want to reference each property of the json object in a different file
This is what I'd like to do (but the validations are not working for me):
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties":
{
"id": { "$ref": "type/common.json#def_id" },
"title": { "$ref": "type/book.json#def_title" }
}
}
...
# type/common.json
{
"def_id":{
"id": {
"type": "string",
"format": "uuid",
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
}
}
}
# type/book.json
{
"def_title":{
"title": {
"anyOf": [
{
"type": "null"
},
{
"type": "string"
}
]
}
}
}
If you could help me I would be very grateful
I was referencing it wrong, it's work
{
"id": { "$ref": "type/common.json#/def_id/id" },
"title": { "$ref": "type/book.json#/def_title/title" }
}
this works for me

Json Schema Polymorphism Validate with anyOf

I've got a Pet object that could be either a dog or a cat
Depending on what noise they make I'd like to then be able to validate other fields.
schema:
{
"$id": "http://example.com",
"definitions": {
"pet": {
"type": "object",
"properties": {
"noise": {
"enum": [
"bark",
"meow"
]
}
}
},
"dog": {
"$ref": "#/definitions/pet",
"properties": {
"noise": {
"const": "bark"
},
"tail": {
"enum": [
"short",
"long"
]
}
}
},
"cat": {
"$ref": "#/definitions/pet",
"properties": {
"noise": {
"const": "meow"
},
"tail": {
"enum": [
"wavy",
"slinky"
]
}
}
}
},
"type": "object",
"properties": {
"pets": {
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/dog",
"$ref": "#/definitions/cat"
}
]
}
}
}
}
This works when running the following json through:
{"pets":[{"noise":"meow","tail":"wavy"}]}
but not when running:
{"pets":[{"noise":"bark","tail":"long"}]}
[$.pets[0].tail: does not have a value in the enumeration [wavy, slinky], $.pets[0].noise: must be a constant value meow]
or
{"pets":[{"noise":"bark","tail":"long"},{"noise":"meow","tail":"wavy"}]}
[$.pets[0].tail: does not have a value in the enumeration [wavy, slinky], $.pets[0].noise: must be a constant value meow]
I can get this working by using if/else in the json schema, but requires another type to avoid a circular dependency:
"petWithConstraints": {
"$ref":"#/definitions/pet",
"allOf": [
{
"if": {
"properties": {
"noise": {
"const": "bark"
}
}
},
"then": {
"$ref": "#/definitions/dog"
}
},
{
"if": {
"properties": {
"noise": {
"const": "meow"
}
}
},
"then": {
"$ref": "#/definitions/cat"
}
}
]
}
}
This means for every new definition it also requires another if statement.
Is there a better method of doing this? (without the extra definition/if statement)
For those that come across this, this was a syntactical error.
Each ref should have been in it's own code block.
The corrected part of the schema looks like the following:
"properties": {
"pets": {
"type": "array",
"items": {
"anyOf": [
{ // Notice each $ref is encapsulated in it's own block
"$ref": "#/definitions/cat"
},
{
"$ref": "#/definitions/dog"
}
]
}
}
}
Running the following json through gave expected results
{"pets":[{"noise":"bark","tail":"long"},{"noise":"meow","tail":"wavy"}]}
[]
{"pets":[{"noise":"bark","tail":"long"},{"noise":"meow","tail":"wavy"},{"noise":"meow","tail":"slinky"},{"noise":"bark","tail":"short"}]}
[]
{"pets":[{"noise":"bark","tail":"long"},{"noise":"meow","tail":"wavy"},{"noise":"meow","tail":"slinky"},{"noise":"bark","tail":"short"},{"noise":"meow","tail":"short"}]}
[$.pets[4]: should be valid to one and only one of the schemas ]

Json schema not validating/working with sub schema

I have json shown below. I want to get it work against a list of zoo which will must have zoo_unique_code. But can have animal or bird or both or none of them. But i want to validate it with sub schema if it have animal or bird e.g bird/animal_id. It seems subschema is not working.
{
"type": "object",
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"animal_id": {
"type": "string",
"maxLength": 24
},
"bird_id": {
"type": "string",
"maxLength": 50
},
"zoo_bird_and_animal": {
"type": "object",
"anyOf": [{
"properties": {
"zoo_bird": {
"type": "object",
"required": [
"zoo_bird_id"
],
"properties": {
"zoo_bird_id": {
"$ref": "#/definitions/bird_id"
}
}
}
}
}, {
"properties": {
"zoo_animal": {
"type": "object",
"required": [
"zoo_animal_id"
],
"properties": {
"zoo_animal_id": {
"$ref": "#/definitions/animal_id"
}
}
}
}
}
]
}
},
"properties": {
"zoo_list": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": [
"zoo_unique_code"
],
"minProperties": 1,
"properties": {
"zoo_unique_code": {
"type": "string",
"enum": [
"NEWYORKZOO",
"PARISZOO"
]
}
},
"$ref": "#/definitions/zoo_bird_and_animal"
}
}
}
}
And testing it with
{
"zoo_list": [
{
"zoo_unique_code": "NEWYORKCODE",
"zoo_bird": {
"zoo_bird_id": "newid"
}
}
]
}
Any suggestion will be appreciated.
As far as i can interpret your schema, it seems you want to use a combining schema at the end, rather then having the ref in the same items part.
With this allOf the schema needs to be a valid object like defined in items and like the ref in the definitions
Also the other error comes from using anyOf instead of allOf.
With anyOf, it needs to be valid against either the first or the second of the schemas, as both validate against a object, even when the first is invalid, the second is valid, so everything is valid. This could also be changed with additionalProperties, but then it does not work the way you nested it.
anyOf: As long as a value validates against either of these schemas, it is considered valid against the entire combined schema.
- combining-schemas
You would also want to use allOf here, so it must validate against all, or rewrite this condition to not use an object here.
With that anyOf to allOf modification, your given data now also validates the bird_id:
And i think you are not using draft-04 here, looks like draft-7.
{
"type": "object",
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"animal_id": {
"type": "string",
"maxLength": 24
},
"bird_id": {
"type": "string",
"maxLength": 50
},
"zoo_bird_and_animal": {
"type": "object",
"allOf": [
{
"properties": {
"zoo_bird": {
"type": "object",
"required": [
"zoo_bird_id"
],
"properties": {
"zoo_bird_id": {
"$ref": "#/definitions/bird_id"
}
}
}
}
},
{
"properties": {
"zoo_animal": {
"type": "object",
"required": [
"zoo_animal_id"
],
"properties": {
"zoo_animal_id": {
"$ref": "#/definitions/animal_id"
}
}
}
}
}
]
}
},
"properties": {
"zoo_list": {
"type": "array",
"minItems": 1,
"items": {
"allOf": [
{
"type": "object",
"required": [
"zoo_unique_code"
],
"minProperties": 1,
"properties": {
"zoo_unique_code": {
"type": "string",
"enum": [
"NEWYORKZOO",
"PARISZOO"
]
}
}
},
{
"$ref": "#/definitions/zoo_bird_and_animal"
}
]
}
}
}
}
Invalid data, see: invalid:
{
"zoo_list": [
{
"zoo_unique_code": "NEWYORKCODE",
"zoo_bird": {
"zoo_bird_id": "newidnewidnewidnewidnewidnewidnewnewidnewidnewidnewidnewidnewidnew"
}
}
]
}
Valid data, see valid:
{
"zoo_list": [
{
"zoo_unique_code": "NEWYORKZOO",
"zoo_bird": {
"zoo_bird_id": "newid"
}
}
]
}

Is it possible to create a JSON Schema with allOf (multiple if and then) and $ref?

I am trying to create a complex schema that will check for the value of a property and then validate according to the value of that same property. I am wondering if it's possible to use $ref and allOf in the same schema and if so, how? I am having some trouble getting this to work. It may be important to note that I am using AJV. Please see my code below
{
"$ref": "#/definitions/Welcome",
"definitions": {
"Welcome": {
"properties": {
"auth": {
"type": "string",
"enum": ["oauth1","oauth2"]
},
"environment": {
"$ref": "#/definitions/Environment"
}
}
},
"Environment": {
"properties": {
"dev": {
"type": "object"
}
}
},
"Oauth1": {
"type": "object",
"properties": {
"temporary_credentials": {
"type": "string"
}
}
},
"Oauth2": {
"type": "object",
"properties": {
"auth_url": {
"type": "string"
}
}
}
},
"allOf": [
{
"if": {
"auth": {
"const": "oauth1"
}
},
"then": {
"environment": {
"dev": {
"$ref": "#/definitions/Oauth1
}
}
}
},
{
"if": {
"auth": {
"const": "oauth2"
}
},
"then": {
"environment": {
"dev": {
"$ref": "#/definitions/Oauth2
}
}
}
}
]
}
A sample json input to be validated against this schema would be something like this
{
"auth": "oauth1",
"environment": {
"dev": {
"temporary_credentials": "xyzzy"
}
}
}
I feel like there might be an error in my "then" statements or simply the placement of the allOf. The error I would get is something like this "$ref: keywords ignored in schema at path "#"".
In schema version up to and including draft7, once you use "$ref", all other keywords in that level of the schema are ignored. That's what the error is telling you: because you used $ref, other keywords are ignored.
If you only want to use a $ref at the root level, the trick is to wrap it in an "allOf".
But since you already have an allOf at the root level, you can just add the $ref as another branch of the allOf and it will work.
That would look like:
"allOf": [
{
"$ref": "#/definitions/Welcome",
},
{
"if": {
"auth": {
"const": "oauth1"
}
etc.
Note: in the schema you posted, you have two unclosed strings "#/definitions/Oauth1 and "#/definitions/Oauth2. If you had that in your real schema it would be invalid JSON.

JSON schema "switch" not working

I'm trying to use the switch attribute of JSON schema v5 using the https://github.com/epoberezkin/ajv library.
I have the following schema but it's not working.
{
"type": "array",
"id": "http://localhost/drinks.json",
"items": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"switch": [
{
"if": {
"properties": {
"type": {
"enum": ["coffee", "tea"]
}
}
},
"then": {
"$ref": "http://localhost/caffeinated.json"
}
},
{
"then": {
"$ref": "http://localhost/decaf.json"
}
}
]
}
}
}
}
}
To be clear, the $refs work just fine without the switch statement. For example, this gets validated correctly:
{
"type": "array",
"id": "http://localhost/drinks.json",
"items": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "http://localhost/caffeinated.json"
},
{
"$ref": "http://localhost/decaf.json"
}
]
}
}
}
}
}
But the first schema doesn't work, and by "doesn't work" I mean it doesn't even trigger valid/invalid. Did I do something wrong?