JSON Schema Dependency Between Properties in Separate Objects - json

Creating a schema for the following document:
{
"name": "billy",
"foo": {
"this": "something"
},
"bar": {
"that": "something"
}
}
I want the schema to require $.bar.that if $.foo.this is present.
Tried a dependency on this with a JSON pointer to that.
Tried an if/then block, but I couldn't figure out how to say "this is present" in the if portion of the block.

dependencies only works with the current object, so it has to be done with if/then. You need to use properties to set required at every level.
{
"if": {
"properties": {
"foo": { "required": ["this"] }
},
"required": ["foo"]
},
"then": {
"properties": {
"bar": { "required": ["this"] }
},
"required": ["bar"]
}
}

Related

How to use dependentRequired with relative reference in JSON schema

I have below JSON schema in my app, and i am using NewtonSoft JSON schema validation library to validate user JSON against my schema.
The rules that i need to set are -
if user sets property2 in the JSON then property3 should also exist and subProperty2 should also exist underneath property3.
if user does not set property2 then property3 is not required.
I used dependentRequired for this with relative reference using period(.) but that did not work with NewtonSoft package. I tried in 2 different ways, both without expected result.
{
"type": "object",
"additionalProperties": false,
"properties": {
"property1": {
...
},
"property2": {
...
},
"property3": {
"type": "object",
"additionalProperties": false,
"properties": {
"subProperty1": {
...
},
"subProperty2": {
...
},
}
}
},
"required": [
"property1"
]
}
//try 1
"dependentRequired": {
"property2": [ "property3.subProperty2" ]
},
//try 2
"dependentRequired": {
"property2": {
"required": [ "property3" ],
"property3": {
"required": [ "subProperty2" ]
}
}
}
Can someone help me with this?
The dot notation used in the first try is not supported in JSON Schema.
The second try is the right idea, but since you are using a schema, you need to use dependentSchemas instead of dependentRequired. Also, you are missing the properties keyword to make that a valid schema.
"dependentSchemas": {
"property2": {
"required": [ "property3" ],
"properties": {
"property3": {
"required": [ "subProperty2" ]
}
}
}
},

Cannot validate recursive schema

I'm stuck on writing a correct json schema for my data.
Condition:
directory is an recursive object with optional property "$meta"
file can be string or object
if file is object it can have optional property "$meta" with oneOf "$data" or "$stringData" not both.
JSON Data:
{
"dir1": {
"dir1A": {},
"dir1B": {
"dir1B01": {
"dir1B0101": {},
"dir1B0102": {},
"dir1B0103": {},
"file1B.txt": {
"$meta": {},
"$data": "ooo",
"$stringData": "Netus et malesuada" // here should failed
}
}
}
},
"dir2": {
"file2.txt": "dolor sit amet" // here should OK but failed
},
"file1.txt": "Lorem Ipsum"
}
Schema:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/dir.schema.json",
"type": "object",
"patternProperties": {
"^[^\\/?%*:|\"<>]+$": { // failed if name consist of invalid chars
"$ref": "#/$defs/directory"
}
},
"additionalProperties": false,
"$defs": {
"file": {
"oneOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"$data": {
"type": "string",
"description": "base64 encoded data"
},
"$stringData": {
"type": "string"
}
},
"oneOf": [
{
"not": {
"anyOf": [
{
"required": [
"$data",
"$stringData"
]
}
]
}
},
{
"allOf": [
{
"not": {
"required": [
"$data"
]
}
},
{
"not": {
"required": [
"$stringData"
]
}
}
]
}
]
}
]
},
"directory": {
"anyOf": [
{
"$ref": "#/$defs/file"
},
{
"type": "object",
"properties": {
"$meta": {
"type": "object",
"additionalProperties": true,
"properties": {
"createdAt": {
"type": "string"
},
"size": {
"type": "integer"
},
}
}
},
"patternProperties": {
"^[^\\/?%*:|\"<>]+$": {
"$ref": "#"
}
}
}
]
}
}
}
Live Demo: https://www.jsonschemavalidator.net/s/HNFtNfRw
Thank you for help
There are a few issues, so let's walk through deriving this schema one step at a time.
Let's start with the object representation of a file. Here's the easy part.
{
"type": "object",
"properties": {
"$meta": { "$ref": "#/$defs/meta" },
"$data": { "type": "string" },
"$stringData": { "type": "string" }
}
}
Then we need to add the assertion that one and only one of $data or $stringData is required. The oneOf keyword can express this cleanly.
"oneOf": [
{ "required": ["$data"] },
{ "required": ["$stringData"] }
]
Only one of the schemas can validate to true. If both properties are present, the schema will fail.
Next, let's define a file can be represented as a string or an object.
{
"anyOf": [
{ "type": "string" },
{ "$ref": "#/$defs/object-file" }
]
}
Notice that we are using anyOf instead of oneOf in this case. anyOf passes validation as soon as one of the schemas is valid. If the first one passes validation, it doesn't need to check the second one. If oneOf is used, the validator needs to check both schemas because oneOf requires that only one of the schemas passes validation and the rest fail. Because one of these schemas is a string and the other is an object, it's impossible for any JSON data to be both a string an object. Therefore, using oneOf only makes the validation less efficient.
That covers files, let's do directories next.
{
"type": "object",
"properties": {
"$meta": { "$ref": "#/$defs/meta" }
},
"patternProperties": {
"^[^\\/?%*:|\"<>]+$": { "$ref": "#/$defs/file-or-directory" }
},
"additionalProperties": false
}
The recursive reference part is pretty simple, but we have a problem with the $meta property. $meta matches the regular expression in patternProperties, so $meta will need to validate against #/$defs/meta and #/$defs/file-or-directory, which is not what you want. What you need is something like an additionalPatternProperties keyword, but since that doesn't exist, you need to change your patternProperties regex to exclude $meta. Something like this, "^[^\\/?%*:|\"<>]+(?<!\\$meta)$".
Now all that's left is a schema to express that something is a file or a directory.
"anyOf": [
{ "$ref": "#/$defs/file" },
{ "$ref": "#/$defs/directory" }
]
Again, we want to use anyOf, but there's a bit more to this than you might notice at first glance. The problem is that something that matches the file schema will also match the directory schema. So, we can't use oneOf and the file schema needs to come first. If the directory schema was first, it would incorrectly validate files as directories. When the file schema comes first, it checks if it's a file first and the false positive that we would get from the directory schema becomes irrelevant.
I'll leave it to you to put the pieces together into a full schema, but leave a comment if you have any issues.

Adding ref path to required in if/then [json]

Please I need help, I have a large file which I need to validate some data. For fields under same properties it is working using code below. But I also have fields which are not under the same "properties".
"if": {
"properties": {
"type": { "const": "one" }
}
},
"then": { "required": ["test"] } }
So, what I'm doing is adding a reference path to the required statement. I tried this
"then": { "required": {"$ref" : "#/path/example" }}
but it didn't work. Any ideas if I can use reference path inside required and if yes how?
I think what you are trying to express is that if /type is "one" then /path/example is required.
So, these should be valid:
{
"type": "one",
"path": { "example": 42 }
}
{
"type": "two",
"path": { "foo": 42 }
}
And, this should not be valid:
{
"type": "one",
"path": { "foo": 42 }
}
Here's how we can do that:
"if": {
"properties": {
"type": { "const": "one" }
}
},
"then": {
"properties": {
"path": { "required": ["example"] }
},
"required": ["path"]
}
You need to establish the path using properties (potentially several nested properties if you need to go more than two levels) and use required at each level.
(If I'm still no understanding your question, let me know and we can try again)

Conditionally apply sub-schema, based on another file's property

Does json-schema allow to have conditional sub-schema based on the property value from another file?
Example:
Jsons:
another.json
{
"honey": "honey"
}
main.json
{
"hello": "abc"
}
Schema
{
"type": "object",
"properties": {
"hello": {
"type": "string"
}
},
"required": ["hello"],
"if": {
"properties": {
"another.json#/honey": { -->> is this possible
"const": "honey"
}
}
},
"then": {
"properties": {
"hi": {
"type": "string"
}
},
"required": ["hi"]
}
}
The main.json should fail validation screaming "hi" is required since, another.json has "honey" as value.
Now, changing another.json to
{
"honey": "not_honey"
}
Should pass the validation for main.json
The question is
No. JSON Schema has no notion of files, only "a JSON instance", of which only one can be validated at a time. You would need to combine them yourself in some way.

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.