I have to validate json Array object based on property enum value.
{
"college":[
{
"staffDetails": {
"etype": "hod",
"workHandling": "department"
}
},
{
"staffDetails": {
"etype": "professor",
"workHandling": "CSE"
"DataStructure": true
"Maths": false
}
}
]
}
My json schema
{
"type": "object",
"properties": {
"college": {
"type": "array",
"minItems": 2
"items":{
"$ref": "#definitions/deatils"
}
}
},
"required": ["college"],
"definition": {
"details": {
"type": "object",
"properties": {
"etype": { "enum": ["hod","professor","assistant professor"], "type":"string"},
"workHandling": {"type": "string"}
"DataStructure": {"type": "boolean"}
"Maths": {"type": "boolean"}
},
"oneOf": [
{
"properties" {
"etype": { "const": "hod"}
},
"required": ["workHandling"]
},
{
"properties" {
"etype": { "const": "professor"}
},
"required": ["workHandling", "DataStructure", "Maths"]
}
]
}
}
}
Based on "etype" inside the array object, that Array object property should marked are required.
but the schema definition is not working as expected, its expecting all ["workHandling", "DataStructure", "Maths"] in array object index 0 and 1 and so on even etype is hod.
Any suggestion, where iam missing of any other better solution...
Related
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.
I want to check that "obj" contains at least one value "test". But keys in the "obj" are random.
Json:
{
"title": "doc",
"obj": {
"xxxx-random": "test",
"zzzz-random": "wol"
}
}
My idea was to use oneOf but it doesn't work as I expected:
Schema Json:
{
"properties": {
"title": {
"type": "string"
},
"obj": {
"type": "object",
"oneOf": [
{
"contains": {
"properties": {
"type": "string",
"desc":"test"
}
}
}
]
}
}
}
You can do that with slightly-mindbending uses of the "not" keyword. That is, we can construct a schema that asserts:
the property "obj" must exist
"obj"'s value is an object, and there must be at least one property
and it cannot be true that all properties under "obj" do NOT match "test"
{
"type": "object",
"required": ["obj"],
"properties": {
"obj": {
"type": "object",
"minProperties": 1,
"not": {
"additionalProperties": { "not": { "const": "test" } }
}
}
}
}
I'd like to be able to manage a json array of 'objects' where each object has a type and properties, and there's a schema error if a mandatory property is missing from an object.
This is my attempt to do this (not including the array part) declaring two object types and saying the object in the json can be either of those types:
{
'definitions':
{
'typeone':
{
'type': 'object',
'properties':
{
'xtype': {'type':'string', 'const':'typeone'},
'num' : {'type':'number'}
},
'required':['xtype', 'num'],
'additionalProperties':false
},
'typetwo':
{
'type': 'object',
'properties':
{
'xtype': {'type':'string', 'const':'typetwo'},
'str' : {'type':'string'}
},
'required':['xtype', 'str'],
'additionalProperties':false
}
},
'anyOf':
[
{ '$ref': '#/definitions/typeone' },
{ '$ref': '#/definitions/typetwo' },
]
}
However, if I feed it json which fails because a mandatory property is missing from an object like this:
{
'xtype': 'typeone'
}
...it errors with JSON does not match any schemas from 'anyOf'. - I can see the reason is that it doesn't know to try to match on the xtype, instead it just considers xtype of 'typeone' invalid and looks to others.
Is there a better way to do anyOf which will hard-match based on one property value (like a 'switch') then give errors about missing other mandatory properties for that object type?
It gets a lot more verbose, but you can use if/then to switch validation based on the "xtype" property.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"allOf": [
{
"if": {
"type": "object",
"properties": {
"xtype": { "const": "typeone" }
},
"required": ["xtype"]
},
"then": { "$ref": "#/definitions/typeone" }
},
{
"if": {
"type": "object",
"properties": {
"xtype": { "const": "typetwo" }
},
"required": ["xtype"]
},
"then": { "$ref": "#/definitions/typetwo" }
}
],
"definitions": {
"typeone": {
"type": "object",
"properties": {
"xtype": {},
"num": { "type": "number" }
},
"required": ["num"],
"additionalProperties": false
},
"typetwo": {
"type": "object",
"properties": {
"xtype": {},
"str": { "type": "string" }
},
"required": ["str"],
"additionalProperties": false
}
}
}
With a small change to the model, you could use dependencies to get a much simpler cleaner schema. Instead of having an "xtytpe" property, you can have a property corresponding to the name of the type. For example, { "typeone": true }.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"dependencies": {
"typeone": {
"type": "object",
"properties": {
"typeone": {},
"num": { "type": "number" }
},
"required": ["num"],
"additionalProperties": false
},
"typetwo": {
"type": "object",
"properties": {
"typetwo": {},
"str": { "type": "string" }
},
"required": ["str"],
"additionalProperties": false
}
}
}
What I am asking myself is if I can cascade multiple 'oneOf' or maybe there is a better way to make my Cases Valid.
I am Trying to validate the following:
Use definition of ObjectA or ObjectB as Single Objects or an Array of a them
Case 1:
Using only definition of ObjectA
{
"X": "test"
}
Case 2:
Using only definition of ObjectB
{
"Y": "test"
}
Case 3:
Using definition of ObjectA or ObjectB in an Array
[
{
"X": "test"
},
{
"Y": "test"
}
]
Case 4:
Using definition of ObjectA twice in an Array
[
{
"X": "test"
},
{
"X": "test"
}
]
Schema:
I tryed using this schema, the IntelliSense of MonacoEditor is working well but I still get the Error/Warning: "Matches multiple schemas when only one must validate."
{
"definitions": {
"objectA": {
"type": "object",
"properties": {
"X": {
type: "string"
}
}
},
"objectB": {
"type": "object",
"properties": {
"Y": {
type: "string"
}
}
}
},
"oneOf":
[
{
"oneOf":
[
{
"$ref": "#definitions/objectA"
},
{
"$ref": "#definitions/objectB"
}
]
},
{
"type": "array",
"items":
{
"oneOf":
[
{
"$ref": "#definitions/objectA"
},
{
"$ref": "#definitions/objectB"
}
]
}
}
]
}
Error/Warning:
"Matches multiple schemas when only one must validate."
The problem is that your X property in objectA and Y property en objectB are not required, so an empty object, that is { }, validates against both.
Also, if you want an array with objectA and objectY to be valid, you need to use anyOf instead of oneOf.
{
"definitions": {
"objectA": {
"type": "object",
"properties": {
"X": {
"type": "string"
}
},
"required": ["X"]
},
"objectB": {
"type": "object",
"properties": {
"Y": {
"type": "string"
}
},
"required": ["Y"]
}
},
"oneOf":
[
{"$ref": "#/definitions/objectA"},
{"$ref": "#/definitions/objectB"},
{
"type": "array",
"minItems": 1,
"items":
{
"anyOf":
[
{"$ref": "#/definitions/objectA"},
{"$ref": "#/definitions/objectB"}
]
}
}
]
}
I added the minItems if you don't want an empty array to validate.
I have a some kind JSON structure within an array. The requirement is:
1. All the JSON object within the array can be optional.
2. Each JSON can have its own set of properties and can be complex and nested.
3. Each JSON object will have a set of mandatory attributes.
How to create a schema for such JSON. Will uisng anyOf or definitions will be helpful?
Updated: I have an array of JSON objects, where each object can have different attributes. The only attribute that will be common is 'type' with valid values as: electronics or furniture or finance. So my question is how to derive a schema?
Example
{
"list": [
{
"type": "electronics"
},
{
"type": "furniture"
},
{
"accessRights": "readOnly",
"rules": ['print','copy'],
"type": "finance"
}
}
Solution
{
"properties": {
"list": {
"type": "array",
"items": {
"type": "object",
"required": ["type"],
"properties": {
"type": {
"type": "string",
"enum": ["electronics", "furniture", "finance"]
}
},
"anyOf": [{
"properties": {
"type": {
"enum": ["electronics"]
}
}
}, {
"properties": {
"type": {
"enum": ["furniture"]
}
}
}, {
"properties": {
"type": {
"enum": ["finance"]
},
"accessRights": {
"type": "string"
},
"rules": {
"type": "array"
}
}
}]
}
}
}
}