Variable object names in JSON schema - json

I'm attempting to define the following in JSON schema:
"codenumber": {
"12345": [
{
"prop1": "yes",
"prop2": "no"
}
]
}
The codenumber object contains a property "12345" which is always a string number that contains an array. The number value can change however, so I cannot simply define this like so:
"codenumber": {
"type": "object",
"properties": {
"12345": {
"type": "array",
"items": {
"type": "object",
"properties": {
"prop1": { "type": "string" },
"prop2": { "type": "string" }
}
}
}
}
}
Any way I can just define the first property name to be of any type of string?

You can use "patternProperties" instead of "properties":
"codenumber": {
"type": "object",
"patternProperties": {
"^[0-9]+$": {
"type": "array",
"items": {
"type": "object",
"properties": {
"prop1": { "type": "string" },
"prop2": { "type": "string" }
}
}
}
}
}

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.

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

Ensure one property is not empty in JSON schema

For the given schema below, is it possible to ensure that at least one property contains a value (ie, minLength is 1):
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"fundRaiseId": {
"type": "string"
},
"productTypeId": {
"type": "string"
},
"businessLineId": {
"type": "string"
}
}
}
So this would pass validation:
{
"fundRaiseId": "x"
}
And this would fail as no values are present:
{
"fundRaiseId": "",
"productTypeId": "",
"businessLineId": ""
}
I would try something like
{
"allOf": [{
"type": "object",
"properties": {
"fundRaiseId": {
"type": "string"
},
"productTypeId": {
"type": "string"
},
"businessLineId": {
"type": "string"
}
}
}, {
"anyOf": [{
"properties": {
"fundRaiseId": {
"$ref": "#/definitions/nonEmptyString"
}
}
}, {
"properties": {
"productTypeId": {
"$ref": "#/definitions/nonEmptyString"
}
}
}, {
"properties": {
"businessLineId": {
"$ref": "#/definitions/nonEmptyString"
}
}
}]
}],
"definitions": {
"nonEmptyString": {
"type": "string",
"minLength": 1
}
}
}
Explanation: the JSON to be validated should conform to 2 root-level schemas, one is your original definition (3 string properties). The other one contains 3 additional sub-schemas, each defining one of your original properties as non-empty string. These are wrapped in an "anyOf" schema, so at least one of these should match, plus the original schema.
Is it a requirement that you allow the values to be empty? You can write a much cleaner schema if you requiring that all the strings are non-empty.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"fundRaiseId": { "$ref": "#/definitions/non-empty-string" },
"productTypeId": { "$ref": "#/definitions/non-empty-string" },
"businessLineId": { "$ref": "#/definitions/non-empty-string" }
},
"anyOf": [
{ "required": ["fundRaiseId"] },
{ "required": ["productTypeId"] },
{ "required": ["businessLineId"] }
],
"definitions": {
"non-empty-string": {
"type": "string",
"minLength": 1
},
}
}

json schema for dynamic array

I have the following json
{
"Dettype": "QTY",
"Details": [
{
"12568": {
"Id": 12568,
"qty":1,
"Freq":"2",
"Option": 0,
"promote":"yes"
},
"22456": {
"Id": 22456,
"qty":2,
"Freq":"3",
"Option": 1,
"promote":"no"
}
}
]
}
For the above json i need to write a json schema file which will valdiates the request.
but the problem is in array the key value for each item changes dynamically. If it is some constant value i can write but don't how to do the dynamic pattern
JSON schema i got
{
"type": "object",
"additionalProperties": true,
"properties": {
"Dettype": {
"type": "string"
},
"Details": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"**DYNAMIC VALUE**": {
"type": "object",
"additionalProperties": true,
"properties": {
"Id": {
"type": "integer"
},
"qty": {
"type": "integer"
},
"Freq": {
"type": "string"
},
"Option": {
"type": "integer"
},
"promote": {
"type": "string"
}
}
}
}
}
}
}
}
Can some one tell what changes need to be done for schema
This is what patternProperties is for.
Here it seems your object member keys are always digits; therefore you can write things like this:
"type": "object",
"patternProperties": {
"^\\d+$": {
"type": "object",
"etc": "etc"
}
}
You can also use additionalProperties if you want all properties to match some schema:
{
"type": "object",
"additionalProperties": {
"type": "object",
"etc": "etc"
}
}