Json Schema Dependencies with Object Definitions - json

When using json schema 7 is it possible to set a dependency on an object definition references rather individual fields?
For example I have a type string and properties object. Depending on the type the properties object fields will be different but I don't want to set dependancy of every possible field. That's rather tedious e.g.
{
"$id": "https://sibytes.datagovernor.com/dataset.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "dataset",
"type": "object",
"properties": {
"dataset_type": {
"type": "string",
"description": ""
},
"dataset_properties": {
<< BASICALLY HERE I WANT THE REFERENCE OBJECT DEFINITION TO BE DEPENDENT ON THE DATASET TYPE ABOVE>>
"$ref": "http://example.com/tableproperties.schema.json"
"$ref": "http://example.com/tableproperties.fileproperties.json"
...there will be others.
}
}
}

So turns out this can de done using sub-schema's and predicates
https://json-schema.org/understanding-json-schema/reference/conditionals.html
{
"$id": "https://sibytes.datagovernor.com/dataset.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "dataset",
"type": "object",
"properties": {
"dataset_type": {
"enum": ["Table","View"],
"description": "This is the type of physical object that the dataset describes."
}
},
"dataset_properties": {
"allOf": [
{
"if": {
"properties": {"dataset_type":{"const": "Table"}}
},
"then": {
"properties": {
"dataset_properties" : {
"$ref": "dataset.table.schema.json#/definitions/dataset_properties"
}
}
}
},
{
"if": {
"properties": {"dataset_type":{"const": "View"}}
},
"then": {
"properties": {
"dataset_properties" : {
"$ref": "dataset.view.schema.json#/definitions/dataset_properties"
}
}
}
}
]
},
"required": ["dataset_type","dataset_properties"]
}

Related

How Do I Require that a Sub-Property Must Exist Using JSON Schema?

In JSON Schema, I can use require to ensure that a property exists on the same level of the hierarchy, but I'm having trouble validating for nested ones.
Suppose I have following JSON Schema:
{
"type": "object",
"properties": {
"my_type": {
"type": "string"
},
"t1_data": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
},
"t2_data": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
}
}
}
How would I specify the following validations?
if my_type == "type1", then t1_data.id must exist
if my_type == "type2", then t2_data.id must exist
if my_type is anything else, validation passes
I've tried using the require and anyOf constructs but I could only get them to work at the same level of the hierarchy.
Thanks,
A possible solution is to combine allOf and if-then. It is a little bit verbose but I am not aware of any shorter way. Here is the schema for the case "type1":
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"description": "JSON schema generated with JSONBuddy https://www.json-buddy.com",
"type": "object",
"properties": {
"my_type": {
"type": "string"
},
"t1_data": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
},
"t2_data": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
}
},
"allOf": [
{
"if": {
"properties": {
"my_type": {
"const": "type1"
}
}
},
"then": {
"properties": {
"t1_data": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
},
"required": [ "id" ]
}
}
}
}
]
}
"type2" would be quite the same as next schema in the allOf array.

Is it possible to reference to JSON data itself within a schema definition?

Is it possible to define a JSON schema to check inner data integrity? So that mistakes like "ba" could be detected without further runtime checks.
Data JSON
{
"childNames": ["foo", "bar", "baz"],
"children": [
{
"name": "foo"
},
{
"name": "ba"
}
]
}
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://tbd.com/foo/bar",
"type": "object",
"properties": {
"childNames": {
"type": "array",
"items": { "type": "string" }
},
"children": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"enum": { "$ref": "???" }
}
}
}
}
}
}
If you're using .Net, then you can do this with JsonSchema.Net and JsonSchema.Net.Data.
You'll need to use a custom vocabulary which defines the data keyword.
Your schema will need to change to:
{
"$schema": "https://gregsdennis.github.io/json-everything/meta/data",
"$id": "https://tbd.com/foo/bar",
"type": "object",
"properties": {
"childNames": {
"type": "array",
"items": { "type": "string" }
},
"children": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"data": {
"enum": "#/childNames"
}
}
}
}
}
}
}
Note the data keyword where your ??? was and the different meta-schema in the $schema keyword.
You can try it on https://json-everything.net/json-schema.
Information on how this works is here.
If you're not using .Net, you may need to figure out how to support this vocabulary (or work with the maintainer of the library you're using to do so).
Disclaimer: I'm the creator & maintainer of JsonSchema.Net and the other json-everything libraries.

Json Schema for Aspect Oriented Json files

So my json structure is aspect oriented, meaning that the json is structure in a way that each data is represented by a key and that key will define the structure of its content.
for example:
[
{
"nv": [{ "ln": 123 }]
},
{
"metadata": [{ "name": "nodes" }, { "name": "edges" }]
},
{
"nodes": [{ "#id": 1 }, { "#id": 2 }]
},
{
"edges": [
{ "#id": 1, "nodeId": 1 },
{ "#id": 2, "nodeId": 2 }
]
},
{
"status": [{ "success": true }]
}
]
As shown 3 objects (nv, metadata, status) and based on the name inside of the metadata there will be objects inside the json file.
I tried something like this:
{
"type": "array",
"items": [
{
"type": "object",
"properties": {
"nv": { "type": "array", "items": { "$ref": "#definitions/nv" } }
},
"required": ["nv"]
},
{
"type": "object",
"properties": {
"metaData": {
"type": "array",
"items": { "$ref": "#definitions/metadata" }
}
},
"required": ["metaData"]
},
{
"anyOf": [
{
"type": "object",
"properties": {
"nodes": {
"type": "array",
"items": { "$ref": "#definitions/nodes" }
}
}
},
{
"type": "object",
"properties": {
"edges": {
"type": "array",
"items": { "$ref": "#definitions/edges" }
}
}
},
{
"type": "object",
"properties": {
"edgeAttribute": {
"type": "array",
"items": { "$ref": "#definitions/edgeAttribute" }
}
}
},
{
"type": "object",
"properties": {
"nodeAttribute": {
"type": "array",
"items": { "$ref": "#definitions/nodeAttribute" }
}
}
}
]
},
{
"type": "object",
"properties": {
"status": {
"type": "array",
"items": { "$ref": "#definitions/status" }
}
},
"required": ["status"]
}
],
"definitions": {
"status": {
"type": "object",
"properties": {
"success": { "type": "boolean" }
}
"etc..."
}
}
}
but then if I define an empty array it will be accepted, also it is being accepted if the array only contains one of the 3 required objects.
So is there a way to validate against something like the example using json-schemas?
The real scenario may have more than just 2 objects inside of the metadata that's why I did not use if -> then -> else conditions. if the solution is by using them then please let me know.
The structure of the data makes this a rough one, but there are a few patterns you can use to get the behavior you want. Let's take them one at a time.
Declare an array that can contain any of a number of objects
Generally people use oneOf for this, but I don't recommend that because it can have poor performance and incomprehensible error messages. Usually that means if/then, but in this case you can get good results by defining your items as a single object that only allows one property at a time in each object.
{
"items": {
"type": "object",
"properties": {
"nv": { "$ref": "#/definitions/nv" },
"metadata": { "$ref": "#/definitions/metadata" },
"status": { "$ref": "#/definitions/status" },
"nodes": { "$ref": "#/definitions/nodes" },
"edges": { "$ref": "#/definitions/edges" }
},
"minProperties": 1,
"maxProperties": 1
}
}
Edit: Previously, I recommended dependencies, but then realized that this is better.
Assert that the array contains a required object
To do this, you need to assert that the array contains an object that has a required property.
{ "contains": { "type": "object", "required": ["nv"] } }
You'll have to combine this pattern in allOf to express additional required items.
{
"allOf": [
{ "contains": { "type": "object", "required": ["nv"] } },
{ "contains": { "type": "object", "required": ["metadata"] } },
{ "contains": { "type": "object", "required": ["status"] } }
]
}
Conditionally assert that the array contains a required object
The tricky part here is getting all the nested contains and properties in the if to be able to assert that the "name" is a certain value. The then just uses the same pattern we used above to assert than an object is required in the array.
{
"if": {
"type": "array",
"contains": {
"type": "object",
"properties": {
"metadata": {
"type": "array",
"contains": {
"type": "object",
"properties": {
"name": { "const": "nodes" }
},
"required": ["name"]
}
}
},
"required": ["metadata"]
}
},
"then": { "contains": { "type": "object", "required": ["nodes"] } }
}
The above example shows the "nodes" object being conditionally required. You'll need to repeat this pattern for the "edges" object and combine them with allOf. I suggest making use of definitions to help make this readable.
{
"allOf": [
{ "$ref": "#/definitions/if-metadata-has-nodes-then-require-nodes-object" },
{ "$ref": "#/definitions/if-metadata-has-edges-then-require-edges-object" }
]
}
I would suggest moving each of your "types" into a $defs to be referenced.
{
"$defs": {
"nvObj": {
"type": "object",
"properties": {
"nv": { "type": "array", "items": { "$ref": "#/$defs/nv" } }
},
"required": ["nv"]
},
... // other defitions
}
}
(I've updated the $ref to use $defs instead of definitions as this is the new keyword since draft 7.)
Then you can put many references into a oneOf.
{
"$defs": {
... // from above
},
"type": "array",
"items": {
"oneOf": [
{ "$ref": "#/$defs/nvObj" },
... // all of the other object definitions
]
}
}
You're right to avoid if/then/else for this case. oneOf is the best bet here.

Json schema with keys as data

I have some hard time dealing with json schema.
Suppose this is initial simple json.
[
{
"Field1": 1,
"Description": "Default"
},
{
"Field1": 77,
"Description": "NonDefault"
}
]
And this is schema written which is valid
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"items": {
"type": "object",
"properties": {
"Field1": {
"type": "integer"
},
"Description": {
"type": "string"
}
}
}
}
I wanted to change Json, to use "Field1" as key.
"Field1" is integer value.
I do not know values and how many Field1 there will be in json.
This is final JSON
{
"1": {
"Description": "Default"
},
"77": {
"Description": "NonDefault"
}
}
But how to write json-schema for this JSON?
TimRoberts' answer is almost there. What you want is to describe the item in the patternProperties subschema, similar to how you have items in your example schema.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"patternProperties": {
"^[0-9]*$": {
"type": "object",
"properties": {
"Description": { "type": "string" }
}
}
}
}
You can use "propertyNames" with a "pattern" element to create a regular expression to qualify your key names, instead of enumerating them all. I, personally, prefer your first layout.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"patternProperties": {
"^[0-9]*$": { "type": "string" }
}
}

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" }
}