I must be missing something here, but the below JSON is not getting validated against the schema.
For example, the required attribute from the Java/JavaScript object is never getting enforced as per the schema. (FYI- Every language object may have other attributes or nested object)
However, if I completely remove the definition and directly put under array items each separately, then it validates.
I want to use 'definitions' and gets validated.The reason I have to put all the object in definitions and later I may have to put different language object in oneOf/allOf for other certain validation check.
Online check:
Schema and JSON
JSON
{
"languages": [
{
"lang": "Java",
"trainer": "Peter"
},
{
"lang": "JavaScript",
"enrolled": "42",
"available": "5"
}
]
}
and the Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"languages"
],
"properties": {
"languages": {
"type": "array",
"minItems": 1,
"items": {
"type": "object"
},
"anyOf": [
{
"$ref": "#/definitions/Java"
},
{
"$ref": "#/definitions/JavaScript"
}
]
}
},
"definitions": {
"Java": {
"required": [
"trainer"
],
"properties": {
"lang": {
"enum": [
"Java"
]
},
"trainer": {
"type": "string"
}
}
},
"JavaScript": {
"required": [
"enrolled",
"available"
],
"properties": {
"lang": {
"enum": [
"JavaScript"
]
},
"enrolled": {
"type": "string"
},
"available": {
"type": "string"
}
}
}
}
}
The fixed schema and it works now
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"languages"
],
"properties": {
"languages": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"anyOf": [
{
"$ref": "#/definitions/Java"
},
{
"$ref": "#/definitions/JavaScript"
}
]
}
}
},
"definitions": {
"Java": {
"required": [
"trainer"
],
"properties": {
"lang": {
"enum": [
"Java"
]
},
"trainer": {
"type": "string"
}
}
},
"JavaScript": {
"required": [
"enrolled",
"available"
],
"properties": {
"lang": {
"enum": [
"JavaScript"
]
},
"enrolled": {
"type": "string"
},
"available": {
"type": "string"
}
}
}
}
}
I think that the "array" definition is not correct. The objects that can be added in the array must be refereed in the "items", after you define the type of the items. Something like this:
"languages": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"anyOf": [
{"$ref": "#/definitions/Java"},
{"$ref": "#/definitions/JavaScript"}
]
}
}
I have fixed myself
What I did: I have placed the json object blocks under items section of array.
Related
I have defined the following JSON Schema Grammar which has to take "TypeA" type of elements and none other than that.
{
"$schema": "http://json-schema.org/draft-04/schema",
"title": "Schema definition",
"type": "object",
"scname": "string",
"properties": {
"itemInfo": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/definitions/TypeADefinition"
}
]
}
}
},
"required": [
"itemInfo"
],
"definitions": {
"TypeADefinition": {
"type": "object",
"properties": {
"elementOf": {
"types": {
"enum": [
"TypeA"
]
}
},
"elements": {
"items": {
"oneOf": [
{
"$ref": "#/definitions/TypeAElementDefinition"
}
]
},
"type": "array"
}
}
},
"TypeAElementDefinition": {
"type": "object",
"properties": {
"name": {
"type": "string",
"maxLength": 128
}
},
"required": [
"name"
],
"additionalProperties": false
}
}
}
JSON Object 1:
{
"itemInfo": [
{
"elementOf": "TypeA",
"elements": [
{
"name": "John Doe"
}
]
}
]
}
JSON Object 2:
{
"itemInfo": [
{
"elementOf": "TypeB",
"elements": [
{
"name": "John Doe"
}
]
}
]
}
Both of these JSON objects are getting validated by the JSON grammar that I have defined but only the first JSON object should be validated successfully by the grammar the second JSON should not validated as it has elementOf "TypeB".
Is there anything that is missing in my Schema Grammar?
UPDATE
After updating the schema grammar to the following, I am able to invalidate 2nd JSON object.
{
"$schema": "http://json-schema.org/draft-04/schema",
"title": "Schema definition",
"type": "object",
"scname": "string",
"properties": {
"itemInfo": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/definitions/TypeADefinition"
},
{
"$ref": "#/definitions/TypeBDefinition"
}
]
}
}
},
"required": [
"itemInfo"
],
"definitions": {
"TypeADefinition": {
"type": "object",
"properties": {
"elementOf": {
"type": "string",
"enum": [
"TypeA"
]
},
"elements": {
"items": {
"oneOf": [
{
"$ref": "#/definitions/TypeAElementDefinition"
}
]
},
"type": "array"
}
}
},
"TypeAElementDefinition": {
"type": "object",
"properties": {
"name": {
"type": "string",
"maxLength": 128
}
},
"required": [
"name"
],
"additionalProperties": false
},
"TypeBDefinition": {
"type": "object",
"properties": {
"elementOf": {
"type": "string",
"enum": [
"TypeB"
]
},
"elements": {
"items": {
"oneOf": [
{
"$ref": "#/definitions/TypeBElementDefinition"
}
]
},
"type": "array"
}
}
},
"TypeBElementDefinition": {
"type": "object",
"properties": {
"name": {
"type": "string",
"maxLength": 128
}
},
"required": [
"name"
],
"additionalProperties": false
}
}
}
I have also added a definition for 'TypeB' in the grammar which should validate TypeB elements. But it is not happening.
TypeB element is referring to 'TypeADefinition', which should not happen. What is the issue with the grammar?
Error:
Message: Value "TypeB" is not defined in enum.
Schema path: #/definitions/TypeADefinition/properties/elementOf/enum
I am attempting to validate the following JSON file:
{
"Transaction": {
"Header": {
"Workflow": "Rejection",
"Job-Offer": {
"Offer-Status": "New",
"Datetime-Offered": "2017-12-15T16:00:00",
"Accepted": "YES",
"Datetime-Accepted": "2017-12-15T16:00:00"
}
}
}
}
against the following schema:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Schema",
"description": "Schema",
"$ref": "#/defs/Schema",
"defs": {
"Schema": {
"type": "object",
"additionalProperties": false,
"properties": {
"Transaction": {
"$ref": "#/defs/Transaction"
}
},
"required": [
"Transaction"
],
"title": "Schema"
},
"Transaction": {
"type": "object",
"additionalProperties": false,
"properties": {
"Transaction-Header": {
"$ref": "#/defs/Transaction-Header"
}
},
"required": [
"Transaction-Header"
],
"title": "Transaction"
},
"Transaction-Header": {
"type": "object",
"additionalProperties": false,
"properties": {
"Workflow": {
"type": "string",
"enum": [
"Offer",
"Transfer",
"Acceptance",
"Rejection",
"Cancellation",
"Update"
]
},
"Job-Offer": {
"$ref": "#/defs/JobOffer"
}
},
"required": [
"Workflow"
],
"title": "Transaction-Header"
},
"JobOffer": {
"description": "Job Offer.",
"type": "object",
"additionalProperties": true,
"properties": {
"Offer-Status": {
"type": "string",
"enum": [
"New",
""
]
},
"Datetime-Offered": {
"type": "string",
"format": "date-time"
},
"Accepted": {
"type": "string",
"enum": [
"YES",
"NO",
""
]
},
"Datetime-Accepted": {
"type": "string",
"format": "date-time"
},
"Reason-Rejected": {
"type": "string",
"minLength": 0,
"maxLength": 30
},
"Offer-Cancelled": {
"type": "string",
"enum": [
"YES",
"NO",
""
]
},
"Datetime-Cancelled": {
"type": "string",
"format": "date-time"
}
},
"allOf": [
{ "$ref": "#/defs/JOBACCEPT" },
{ "$ref": "#/defs/JOBREJECT" }
],
"required": [
"Offer-Status"
],
"title": "JobOffer"
},
"JOBACCEPT": {
"properties": {
"Workflow": { "enum": [ "Acceptance" ] }
},
"required": [
"Accepted",
"Datetime-Accepted"
],
},
"JOBREJECT": {
"properties": {
"Workflow": { "enum": [ "Rejection" ] }
},
"required": [
"Reason-Rejected"
],
}
}
}
What I am after is:
If the Workflow of "Acceptance" is selected, the fields under JOBACCEPT are required.
If the Workflow of "Rejection" is selected, the fields under JOBREJECT are required.
I have tried many different combinations of oneOf, allOf, anyOf, if-then-else but nothing seems to work correctly.
Anyone have any ideas what needs to be done?
Re-worked json inline:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/product.schema.json",
"type": "object",
"properties": {
"Transaction": {
"type": "object",
"properties": {
"Transaction-Header": {
"type": "object",
"properties": {
"Workflow": {
"type": "string",
"enum": [
"Offer",
"Transfer",
"Acceptance",
"Rejection",
"Cancellation",
"Update"
]
},
"Job-Offer": {
"type": "object",
"properties": {
"Offer-Status": {
"type": "string",
"enum": [
"New",
""
]
},
"Datetime-Offered": {
"type": "string",
"format": "date-time"
},
"Accepted": {
"type": "string",
"enum": [
"YES",
"NO",
""
]
},
"Datetime-Accepted": {
"type": "string",
"format": "date-time"
},
"Reason-Rejected": {
"type": "string",
"minLength": 0,
"maxLength": 30
},
"Offer-Cancelled": {
"type": "string",
"enum": [
"YES",
"NO",
""
]
},
"Datetime-Cancelled": {
"type": "string",
"format": "date-time"
}
},
"required": [
"Offer-Status"
]
},
"readOnly": true
},
"required": [
"Workflow"
]
}
},
"required": [
"Transaction-Header"
]
}
},
"allOf": [
{
"if": {
"properties": {
"Transaction": {
"properties": {
"Transaction-Header": {
"properties": {
"Workflow": {
"enum": [
"Acceptance"
]
}
},
"required": [
"Workflow"
]
}
}
}
}
},
"then": {
"properties": {
"Transaction": {
"properties": {
"Transaction-Header": {
"properties": {
"Job-Offer": {
"properties": {},
"required": [
"Accepted",
"Datetime-Accepted"
]
}
}
}
}
}
}
}
}
],
"required": [
"Transaction"
]}
You had the right idea. The problem is where you've placed the allOf with your conditionals. You have it in the "JobOffer" schema, but are trying to set constraints on the "Workflow" property which is in the "Transaction-Header" schema. There is no way to reference a property that is higher up in the JSON tree structure, so you need to move the allOf up into the "Transaction-Header" schema so you can set constraints on the "Workflow" property.
Now that it's in the right place, the best way to express your conditional constraints is with if/then. The context of the if/then is now the the "Transaction-Header" schema, so the then schemas needs to not just say what properties are required, they need to declare that those properties are in the "Job-Offer" object.
{
...
"defs": {
...
"Transaction-Header": {
...
"allOf": [
{ "$ref": "#/defs/JOBACCEPT" },
{ "$ref": "#/defs/JOBREJECT" }
]
},
"JOBACCEPT": {
"if": {
"type": "object",
"properties": {
"Workflow": { "enum": ["Acceptance"] }
},
"required": ["Workflow"]
},
"then": {
"properties": {
"Job-Offer": {
"required": ["Accepted", "Datetime-Accepted"]
}
}
}
},
"JOBREJECT": { ... Similar to JOBACCEPT ... }
}
}
You're abstracting everything away into definitions *, so it's tricky to express conditionals that reference things multiple layers deep. If you inline all the definitions, it gets a little easier to see what needs to be done.
The if/then keywords need to be at the level of 'Transaction'. In pseudocode:
"if the property 'Header' exists (i.e. required) and its value is ... (const), then require property ... with value (type=object, required properties=[...], property definitions=...)", and so on.
* by the way, in version 2020-12 the definitions keyword is $defs -- it may work the way you have it, but implementations will be unable to validate the schemas undef defs as they won't recognize them there, so some errors can slip through and be harder to find.
I'm trying to validate a JSON file that contains several nested components of the same type. Each component has a class string property. I'm trying to apply different validation schemas from my definitions to each component based on the value of class. Furthermore, the values of class can be "button-open", "button-close", "icon-message", "icon-...", "container", etc and I would like to apply the same validation schema for all the "buttons-" another to "icons-" and another to the rest.
The code I provide is what I have tried. I have excluded icons- for the shake of simplicity. As you can see I tried to match with a regular expression whenever there is button in class. Then it should constraint the minimum value of children elements to 1.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["results"],
"properties": {
"results": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"anyOf": [
{
"$ref": "#/definitions/genericComponent"
},
{
"$ref": "#/definitions/button"
}
]
}
}
},
"definitions": {
"genericComponent": {
"type": "object",
"$id": "#/definitions/genericComponent",
"required":[
"class",
"children"
],
"properties":{
"class": {
"type": "string",
"description": "predicted class of the element(img, icon..)"
},
"children":{
"type": "array",
"description": "this element can contain more of the same type",
"items":{
"type": "object",
"anyOf":[
{"$ref": "#/definitions/button"},
{"$ref": "#/definitions/genericComponent"}
]
}
}
}
},
"button": {
"$id": "#/definitions/button",
"allOf":[
{"$ref": "#/definitions/genericComponent"},
{"properties": {
"class":{
"type":"string",
"pattern": "(button)"
},
"children":{
"type": "array",
"description": "this element must contain one generic element",
"items":{
"type": "object",
"$ref": "#/definitions/genericComponent"
},
"minItems": 1,
"maxItems": 1
}
}}
]
}
}
}
I also tried to apply the property conditionally as:
{
"type": "object",
"required": ["results"],
"properties": {
"results": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"$ref": "#/definitions/genericComponent"
}
}
},
"definitions": {
"genericComponent": {
"type": "object",
"$id": "#/definitions/genericComponent",
"required":[
"class",
"children"
],
"properties":{
"class":{
"type": "string"
},
"children":{
"type": "array",
"items": {
"type": "object",
"anyOf":[
{"$ref":"#/definitions/genericComponent"},
{"$ref":"#/definitions/button"}
]
}
}
},
"if": {
"properties": {"class": {"pattern": "^button-rect$"}}
},
"then": {
"properties": {
"children":{
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/genericComponent"
},
"minItems": 1,
}
}
},
"else": {
"properties": {
"children":{
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/genericComponent"
}
}
}
}
}
}
}
EDIT: this one sample that pass but it shouldn't as the number of children of one of the buttons is highier of 1.
{"results": [
{
"class": "box",
"children": [
{
"class": "icon",
"children": []
},
{
"class": "button-rect",
"children": [
{
"class": "label",
"children": []
}
]
},
{
"class": "button-round",
"children": [
{
"class": "label",
"children": []
},
{
"class": "label",
"children": []
}
]
}
]
}
]
}
Your approach of applying conditionally was close, but had a few errors.
The reference for #/definitions/button couldn't be resolved, so you might have seen an error.
The regex you used in your if condition was for button-rect while you asked for it to cover button-round in your example. You could of course change it to a non anchored ^button- which sounds like what you want.
Here's the schema.
{
"type": "object",
"required": [
"results"
],
"properties": {
"results": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"$ref": "#/definitions/genericComponent"
}
}
},
"definitions": {
"genericComponent": {
"type": "object",
"$id": "#/definitions/genericComponent",
"required": [
"class",
"children"
],
"properties": {
"class": {
"type": "string"
},
"children": {
"type": "array",
"items": {
"type": "object",
"anyOf": [
{
"$ref": "#/definitions/genericComponent"
}
]
}
}
},
"allOf": [
{
"if": {
"properties": {
"class": {
"pattern": "^button-round$"
}
}
},
"then": {
"properties": {
"children": {
"maxItems": 1
}
}
}
}
]
}
}
}
The then schema is applied to the instance. You don't need to repeat the constraints already expressed by the other part of that schema (with the $id of #/definitions/genericComponent). And by extension, you don't need the else either. (This isn't the same as writing code with an execution flow.)
You can see this schema in action against your JSON instance data at https://jsonschema.dev (link is preloaded with your schema and data)
Let me know if you have any questions. There are a number of issues with the first approach, but given this is the better approach, I'm not sure "fixing" the first one is in scope of this question. =]
I have already created my own schema on intellij environment, and it's working good, but still have problems in auto completion which provides intellij to the schema,
for example if object "car" defined in json schema then intellij can recognize that there's such object in the schema and intellij will give it as suggestion through out coding json, the problem that I'm facing is that the suggestions are contains all the objects that defined in the schema, but the expectations are to get the objects which defined under the scoop of another object
This is some code of my own schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Pipe File",
"type": "object",
"definitions": {
"Pipe": {
"type": "object",
"properties": {
"components": {
"$ref": "#/definitions/components"
}
},
"required": [
"components"
]
},
"components": {
"description": "section which defines the pipes in the file",
"type": "array",
"minItems": 1,
"items": {
"oneOf": [
{
"$ref": "#/definitions/setValuesComponent"
},
{
"$ref": "#/definitions/invokeWebServicesComp"
}
]
}
},
"setValuesComponent": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"enum": [
"setValuesComp"
]
},
"out": {
"type": "object",
"properties": {
"dateFormat": {
"$ref": "#/definitions/setValuesCompOut"
},
"dateTimeFormat": {
"$ref": "#/definitions/setValuesCompOut"
},
"dateFormatBank": {
"$ref": "#/definitions/setValuesCompOut"
}
}
},
"condition": {
}
},
"required": [
"name",
"type",
"out"
]
},
"setValuesCompOut": {
"type": "object",
"properties": {
"exprValue": {
"type": "string"
},
"ctxEntry": {
"type": "string"
},
"value": {
"type": "string"
},
"exprConst": {
"type": "string",
"pattern": "(Class|class)\\.\\w+\\.\\w+"
}
},
"anyOf": [
{
"required": [
"exprValue"
]
},
{
"required": [
"ctxEntry"
]
},
{
"required": [
"value"
]
},
{
"required": [
"exprConst"
]
}
]
},
"invokeWebServicesComp": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"enum": [
"invokeWebServices"
]
},
"mode": {
"enum": [
"innerJoin",
"leftJoin",
"union",
"parallelJoin"
]
},
"method": {
"type": "string"
},
"headers": {
"$ref": "#/definitions/invokeWebServicesCompHeaders"
},
"dataFilePath": {
"type": "string"
},
"restRelativeUrl": {
"type": "string"
},
"in": {
"$ref": "#/definitions/invokeWebServicesCompIn"
},
"out": {
"$ref": "#/definitions/invokeWebServicesCompOut"
}
},
"required": [
"type",
"name",
"out",
"in"
]
},
"invokeWebServicesCompOut": {
"type": "object",
"patternProperties": {
"doc": {
"type": "string",
"pattern": ".+"
}
}
},
"invokeWebServicesCompHeaders": {
"type": "object",
"patternProperties": {
".{1,}": {
"type": "string",
"pattern": ".+"
}
}
},
"invokeWebServicesCompIn": {
"type": "object",
"patternProperties": {
".{1,}": {
"type": "string",
"pattern": ".+"
}
}
},
"properties": {
"pipes": {
"description": "section which defines the mandatory pipes object in the file",
"type": "object",
"patternProperties": {
".{1,}": {
"$ref": "#/definitions/Pipe"
}
}
}
},
"required": [
"pipes"
]
}
}
So what I expected is, when the type of object determined to "setValuesComp", the auto completion will suggest the relevant properties, that's mean it will not suggest "in" property which is belong to "invokeWebServicesComp" not "setValuesComponent".this picture show the auto complete problem in my real environment
Your JSON schema seems to be invalid. The below JSON content should be present inside object type.
"properties": {
"pipes": {
"description": "section which defines the mandatory pipes object in the file",
"type": "object",
"patternProperties": {
".{1,}": {
"$ref": "#/definitions/Pipe"
}
}
}
},
"required": [
"pipes"
]
In your schema, it is present as part of the "definitions". Please make this correction and then check if you are able to get the suggestions.
I have the input json like below,
{"contents":[{"type":"field"},{"type":"field","itemId":"594b9980e52b5b0768afc4e8"}]}
the condition is,
if the type is 'field', then 'itemId' should be the required field
and if the type is 'fieldGroup' or 'subSection', then 'itemId' is optional
This is the Json Schema I tried and its not working as expected,
"type": "object",
"additionalProperties": false,
"properties" : {
"contents" : {
"type" : "array",
"items": {"$ref": "#displayItem" }
}
},
"definitions": {
"displayItem" : {
"id": "#displayItem",
"type": "object",
"items": {
"anyOf": [
{"$ref": "#fieldType"},
{"$ref": "#fieldGroupSubSectionType"}
]
}
},
"fieldType" : {
"id": "#fieldType",
"type": "object",
"additionalProperties": false,
"properties": {
"itemId": {
"type": "string"
},
"type": {
"type": "string",
"enum": ["field"]
}
}
},
"fieldGroupSubSectionType" : {
"id": "#fieldGroupSubSectionType",
"type": "object",
"additionalProperties": false,
"properties": {
"itemId": {
"type": [ "string", "null" ]
},
"type": {
"type": "string",
"enum": [
"fieldGroup",
"subSection"
]
}
}
}
}
Any help / workaround with Sample Json Schema to achieve the above use case is appreciated.
If I understand the description of what you want correctly, then the json example you provide is not valid since it has a type: "field" but does not have an "itemId" property.
Assuming that is true. Instead of using
type: ["string", null]
use the required property.
I changed your schema a bit, instead of having separate definitions I inlined them, but other than that (and the use of required) is the same:
{
"type": "object",
"additionalProperties": false,
"properties": {
"contents": {
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"additionalProperties": false,
"properties": {
"itemId": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"field"
]
}
},
"required": [
"itemId"
]
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"itemId": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"fieldGroup",
"subSection"
]
}
}
}
]
}
}
}
}
Here is your answer with a little cleanup for best practices and style. The trick is that you need to use implication "a implies b <=> (not a) or b". In this case you have "type = field implies itemId is required <=> type is not field or itemId is required".
{
"type": "object",
"properties": {
"contents": {
"type": "array",
"items": { "$ref": "#/definitions/displayItem" }
}
},
"definitions": {
"displayItem": {
"type": "object",
"properties": {
"itemId": { "type": "string" },
"type": { "enum": ["field", "fieldGroup", "subSection"] }
},
"anyOf": [
{ "not": { "$ref": "#/definitions/fieldType" } },
{ "required": ["itemId"] }
]
},
"fieldType": {
"properties": {
"type": { "enum": ["field"] }
}
}
}
}