How to refactorice properties using json schema? - json

I'd like to reference and factor the following json schema in multiple files for a better handling regarding its properties. Files as "$ref": "book.json" and "$ref": "pencil.json" which each file has its own properties and validations (and of course, that it works "addProperties = false"), I have the following schema:
{
"additionalProperties": false,
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string",
"enum": ["Pencil", "Book"]
},
"title": {
"type": "string"
},
"content": {
"type": "string"
},
"domain": {
"type": "string"
},
"img_url": {
"type": "string"
},
"url": {
"type": "string"
}
},
"allOf": [
{
"if": {
"properties": {
"type": {
"enum": ["Book"]
}
}
},
"then": {
"required": [
"domain", "img_url", "url"
],
"not" : {
"anyOf" : [
{ "required" : ["content"] }
]
}
}
},
{
"if": {
"properties": {
"type": {
"enum": ["Pencil"]
}
}
},
"then": {
"required": [
"content"
],
"not" : {
"anyOf" : [
{ "required" : ["domain"] },
{ "required" : ["img_url"] },
{ "required" : ["url"] }
]
}
}
}
]
}
and I would like something like this:
# book.json
{
"properties": {
"content": {
"type": "string"
}
},
"if": {
"properties": {
"type": {
"enum": ["Book"]
}
}
},
"then": {
"required": [
"content"
],
"not" : {
"anyOf" : [
{ "required" : ["domain"] },
{ "required" : ["img_url"] },
{ "required" : ["url"] }
]
}
}
}
# pencil.json
{
"properties":{
"domain": {
"type": "string"
},
"img_url": {
"type": "string"
},
"url": {
"type": "string"
}
},
"if": {
"properties": {
"type": {
"enum": ["Pencil"]
}
}
},
"then": {
"required": [
"domain", "img_url", "url"
],
"not" : {
"anyOf" : [
{ "required" : ["content"] }
]
}
}
}
# properties.json
{
"additionalProperties": false,
"properties": {
"$ref": book.json,
"$ref": pencil.json
}
I would like to simplify it, whether a property is of the book or pencil type then it refers to the file where this file has its properties and validations.

Related

Json schema with nested objects and conditionals

I'm trying to create a json schema for the following structure:
{
"stages":
{
"STAGE1":
{
"stage_type" : "GSX",
"params":
{
"x": "setting_x", <- x mandatory for stage type "GSX"
"y": "setting_y" <- y mandatory for stage type "GSX"
}
},
"STAGE2":
{
"stage_type" : "GSZ",
"params":
{
"z": "setting_z" <- z mandatory for stage type "GSZ"
}
}
}
}
The idea is that "stage_type" is an enum with possible values ["GSX", "GSZ", ...]. The logic that I would like to implement is:
If "stage_type" == "GSX" -> require "params": {"x"} and require "params": {"y"}
If "stage_type" == "GSZ" -> require "params": {"z"}
I'm failing however, at implementing this logic for required properties... Here is how far I've got:
{
"type": "object",
"properties":
{
"stages":
{
"type": "object",
"additionalProperties":
{
"type": "object",
"properties":
{
"stage_type":
{
"type": "string",
"enum": [ "GSX", "GSZ" ]
},
"params":
{
"type": "object",
"properties":
{
"x": { "type": "string" },
"y": { "type": "string" },
"z": { "type": "string" }
},
"additionalProperties": false
}
},
"required": ["stage_type", "params"],
"allOf":
[
{
"if": { "properties": { "stage_type": { "enum": ["GSX"] } } },
"then": { "required": ["x", "y"] }
},
{
"if": { "properties": { "stage_type": { "enum": ["GSZ"] } } },
"then": { "required": ["z"] }
}
]
},
"minProperties": 1,
"uniqueItems": true
}
},
"additionalProperties": false
}
I can't seem to make the nesting of the required fileds in the if-then clauses work... Help would be very much appreciated! :)
In your original schema, the allOf is applied to the schema level at the additionalProperties of "stages". At this level the validator has no scope of the child properties defined at the "params" property. A possible solution could be:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "JSON schema generated with JSONBuddy https://www.json-buddy.com",
"type": "object",
"definitions": {
"params": {
"type": "object",
"properties": {
"x": { "type": "string" },
"y": { "type": "string" },
"z": { "type": "string" }
},
"additionalProperties": false
},
"params_required_z": {
"allOf": [
{ "$ref": "#/definitions/params" },
{ "required": [ "z" ] }
]
},
"params_required_x_y": {
"allOf": [
{ "$ref": "#/definitions/params" },
{ "required": [ "x", "y" ] }
]
}
},
"properties": {
"stages": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"stage_type": {
"type": "string",
"enum": [ "GSX", "GSZ" ]
}
},
"allOf": [
{
"if": {
"properties": {
"stage_type": {
"enum": [ "GSX" ]
}
}
},
"then": {
"properties": {
"params": {
"$ref": "#/definitions/params_required_x_y"
}
}
}
},
{
"if": {
"properties": {
"stage_type": {
"enum": [ "GSZ" ]
}
}
},
"then": {
"properties": {
"params": {
"$ref": "#/definitions/params_required_z"
}
}
}
}
],
"required": [ "stage_type", "params" ]
},
"minProperties": 1,
"uniqueItems": true
}
},
"additionalProperties": false
}

JsonSchema polymorphic object depends on other field

I have following JSONs:
[
{
"type": "message",
"payload": {
"message": "some message"
}
},
{
"type": "image",
"payload": {
"url": "http://example.com/foo/bar"
}
},
{
"type": "video",
"payload": {
"url": "http://example.com/foo/baz"
}
}
]
And I would like to validate:
If type == message then payload should have message
If type in image, video then payload should have url
I figure something like:
{
"type": "object",
"properties": {
"type": {
"enum": ["message", "image", "video"]
},
"payload": {
"type": "object",
"oneOf": [
{
"properties": {
"message": {
"type": "string"
}
},
"required": ["message"]
},
{
"properties": {
"url": {
"type": "string"
}
},
"required": ["url"]
}
]
}
}
}
But this not valid well when e.g.: type is message and payload has url.
you should move the oneOf up to describe the whole message/image/video object, and replace the enum for the "type" property with individual const values across the oneOf schemas:
{
"type": "object",
"oneOf": [
{
"properties": {
"type": {
"const": "message",
},
"payload": {
(message payload schema)
}
}
},
{
"properties": {
"type": {
"const": "image",
},
"payload": {
(image payload schema)
}
},
}
]
}
Using oneOf for this type of thing is terrible for error messaging. Here's an alternative using if/then that's more verbose, but is better for error messaging and performance.
{
"type": "object",
"properties": {
"type": { "enum": ["message", "image", "video"] },
"payload": {
"message": { "type": "string" },
"url": { "type": "string", "format": "uri" }
}
},
"allOf": [
{
"if": {
"properties": {
"type": { "const": "message" }
},
"required": ["type"]
},
"then": {
"properties": {
"payload": { "required": ["message"] }
}
}
},
{
"if": {
"properties": {
"type": { "enum": ["image", "video"] }
},
"required": ["type"]
},
"then": {
"properties": {
"payload": { "required": ["url"] }
}
}
}
]
}

Why is my use of JSON Schema `allOf` keyword not validating correctly?

I am running into more children objects that are not validating correctly (object.actor, object.verb, object.object). I tried looking for any empty schema after changing my if/then structures in the object schema to if/then/else adding the false value for each else. I did not find anything obvious.
JSON -Should fail but doesn't
{
"actor": {
"objectType": "Agent",
"name": "xAPI account",
"mbox": "mailto:xapi#adlnet.gov"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/attended",
"display": {
"en-GB": "attended",
"en-US": "attended"
}
},
"object": {
"objectType": "SubStatement",
"actor": {
"objectType": "should fail",
"name": "xAPI mbox",
"mbox": "mailto:should fail"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/reported",
"display": {
"should fail": "reported",
"en-US": "reported"
}
},
"object": {
"objectType": "Activity",
"id": "should fail"
}
}
}
JSON - Fails at the root level only; substatement values are not checked. assuming empty schema coming from somewhere.
{
"actor": {
"objectType": "Agent",
"name": "xAPI account",
"mbox": "this fails"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/attended",
"display": {
"this fails": "attended",
"en-US": "attended"
}
},
"object": {
"objectType": "SubStatement",
"actor": {
"objectType": "should fail",
"name": "xAPI mbox",
"mbox": "mailto:should fail"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/reported",
"display": {
"should fail": "reported",
"en-US": "reported"
}
},
"object": {
"objectType": "Activity",
"id": "should fail"
}
}
}
JSON SCHEMA (stripped to the bone)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "xAPIValidator",
"description": "Validation schema for xAPI tests",
"type": "object",
"allOf": [
{
"$ref": "#/definitions/Statement"
},
{
"statements": [
{
"$ref": "#/definitions/Statement"
}
]
}
],
"definitions": {
"Statement": {
"additionalProperties": false,
"properties": {
"objectType": {
"type:": "string",
"enum": [
"Agent",
"Activity",
"Group",
"SubStatement",
"StatementRef"
]
},
"id": {
"allOf": [
{
"$ref": "#/definitions/uuid"
}
]
},
"actor": {
"$id": "#actor",
"allOf": [
{
"$ref": "#/definitions/allOfAgentGroup"
}
]
},
"verb": {
"$id": "#verb",
"type": "object",
"properties": {
"id": {
"allOf": [
{
"$ref": "#/definitions/URI"
}
]
},
"display": {
"type": "object",
"allOf": [
{
"$ref": "#/definitions/lang5646"
}
]
}
}
},
"object": {
"$id": "#object",
"type": "object",
"properties": {
"objectType": {
"type:": "string",
"enum": [
"Activity",
"Agent",
"Group",
"SubStatement",
"StatementRef"
]
}
},
"oneOf": [
{
"if": {
"properties": {
"objectType": {
"const": "SubStatement"
}
}
},
"then": {
"$comment": "Substatement object type",
"allOf": [
{
"$ref": "#/definitions/Statement"
}
],
"allOf": [
{
"not": {
"required": [
"id"
]
}
},
{
"not": {
"required": [
"authority"
]
}
},
{
"not": {
"required": [
"stored"
]
}
},
{"required":["actor","verb","object"]}
]
},
"else": false
}
]
}
},
"required": [
"actor",
"verb",
"object"
]
},
"Agent": {
"$id": "#Agent",
"maxProperties": 3,
"allOf": [
{
"$ref": "#/definitions/IFI"
}
]
},
"AnonGroup": {
"$id": "#AnonGroup",
"maxProperties": 3,
"properties": {
"member": {
"type": "array",
"items": [
{
"allOf": [
{
"$ref": "#/definitions/allOfAgentGroup"
}
]
}
]
}
},
"dependencies": {
"objectType": [
"member"
]
},
"required": [
"member"
],
"not": {
"required": [
"mbox"
]
},
"not": {
"required": [
"mbox_sha1sum"
]
},
"not": {
"required": [
"openid"
]
},
"not": {
"required": [
"account"
]
}
},
"IdGroup": {
"$id": "#IdGroup",
"maxProperties": 4,
"properties": {
"member": {
"type": "array",
"items": [
{
"oneOf": [
{
"$ref": "#/definitions/allOfAgentGroup"
}
]
}
]
}
},
"oneOf": [
{
"$ref": "#/definitions/IFI"
}
]
},
"allOfAgentGroup": {
"properties": {
"objectType": {
"type": "string",
"enum": [
"Agent",
"Group"
]
},
"name": {
"type": "string"
}
},
"oneOf": [
{
"if": {
"properties": {
"objectType": {
"const": "Agent"
}
}
},
"then": {
"allOf": [
{
"$ref": "#/definitions/Agent"
}
]
},
"else": false
},
{
"if": {
"properties": {
"objectType": {
"const": "Group"
}
}
},
"then": {
"oneOf": [
{
"allOf": [
{
"$ref": "#/definitions/IdGroup"
}
]
},
{
"allOf": [
{
"$ref": "#/definitions/AnonGroup"
}
]
}
]
},
"else": false
}
]
},
"IFI": {
"oneOf": [
{
"properties": {
"mbox": {
"allOf": [
{
"$ref": "#/definitions/mailto"
}
]
}
},
"required": [
"mbox"
]
},
{
"properties": {
"mbox_sha1sum": {
"type": "string",
"pattern": "\\b[0-9a-f]{5,40}\\b"
}
},
"required": [
"mbox_sha1sum"
]
},
{
"properties": {
"account": {
"properties": {
"homePage": {
"allOf": [
{
"$ref": "#/definitions/URI"
}
]
},
"name": {
"type": "string"
}
},
"required": [
"homePage",
"name"
]
}
},
"required": [
"account"
]
},
{
"properties": {
"openid": {
"allOf": [
{
"$ref": "#/definitions/URI"
}
]
}
},
"required": [
"openid"
]
}
]
},
"mailto": {
"type": "string",
"pattern": "(mailto:)(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")#(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])"
},
"URI": {
"type": "string",
"pattern": "^(https?|ftp|file)://[-a-zA-Z0-9+&##/%?=~_|!:,.;]*[-a-zA-Z0-9+&##/%=~_|]"
},
"uuid": {
"type": "string",
"pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
},
"lang5646": {
"type": "object",
"patternProperties": {
"^((?:(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?:([A-Za-z]{2,3}(-(?:[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?:[A-Za-z]{4}))?(-(?:[A-Za-z]{2}|[0-9]{3}))?(-(?:[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?:[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?:x(-[A-Za-z0-9]{1,8})+))?)|(?:x(-[A-Za-z0-9]{1,8})+))$": {
"type": "string"
}
},
"additionalProperties": false
},
"lang5646string": {
"type": "string",
"pattern": "^((?:(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?:([A-Za-z]{2,3}(-(?:[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?:[A-Za-z]{4}))?(-(?:[A-Za-z]{2}|[0-9]{3}))?(-(?:[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?:[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?:x(-[A-Za-z0-9]{1,8})+))?)|(?:x(-[A-Za-z0-9]{1,8})+))$"
}
}
}
In "$id": "#object" > oneOf > then, you define allOf twice in that JSON object.
The behaviour of duplicate keys in JSON is undefined.
Often the way it's handled is just to take the last occurrence of each key for an object.
You can see this working by having the following schema and an empty object instance: {}
Schema :
{
"allOf": [
false
],
"allOf": [
true
]
}
The JSON library will likely ignore the first allOf, taking the last occurrence, resulting in allways pass validation. Swap the true and false to double confirm.
Any time you've used a specific *of keyword more than once in the same object in the schema (or any key more than once in an object), you'll need to fix it.

jsonschema Draft 0.7 required properties in nested object depending on a value

I have a json file with nested objects.
{
"apiVersion":"0.0.9b",
"apiDate":"18.01.19",
"general":{
"documentType": "invoice",
"references":{
"invoiceId":"123",
"invoiceDate":"01.01.1970",
"creditNoteId":"123",
"creditNoteDate":"01.01.1970"
}
}
}
Now I would like to define that invoiceId and invoiceDate should be required if documentType is invoice, and also the other way arraound (creditNoteId and Date are required if documentType is creditNote).
All other Properties should be optional.
Pseudo-Code:
documentType = invoice
- required: invoiceId, invoiceDate
- optional: creditNoteId, creditNoteDate
documentType = creditNote
- required: creditNoteId, creditNoteDate
- optional: invoiceId, invoiceDate
If i store all properties in the same object I found this working solution:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"apiVersion",
"apiDate"
],
"properties": {
"apiVersion": {
"type": "string",
"description": "The version of the json file"
},
"apiDate": {
"type": "string",
"description": "The date when the json version was published"
},
"general": {
"$ref": "#/definitions/general_identifiers"
}
},
"definitions" : {
"general_identifiers" : {
"type": "object",
"required": [
"documentType"
],
"properties": {
"documentType": {
"enum": [
"invoice",
"creditNote"
]
},
"invoiceId": {
"type": "string"
},
"invoiceDate": {
"type": "string"
},
"creditNoteId": {
"type": "string"
},
"creditNoteDate": {
"type": "string"
}
},
"oneOf": [
{
"$comment": "Invoice",
"properties": {
"documentType": { "enum": ["invoice"] }
},
"required": ["invoiceId", "invoiceDate"]
},
{
"$comment": "CreditNote",
"properties": {
"documentType": { "enum": ["creditNote"] }
},
"required": ["creditNoteId", "creditNoteDate"]
}
]
}
}
}
Is there a way to display this dependency with nested objects used in the above json?
What I tried already was:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"apiVersion",
"apiDate"
],
"properties": {
"apiVersion": {
"type": "string",
"description": "The version of the json file"
},
"apiDate": {
"type": "string",
"description": "The date when the json version was published"
},
"general": {
"$ref": "#/definitions/general_identifiers"
},
"references": {
"type": "object",
"properties": {
"invoiceId": {
"type": "string"
},
"invoiceDate": {
"type": "string"
},
"creditNoteId": {
"type": "string"
},
"creditNoteDate": {
"type": "string"
}
},
"oneOf": [
{
"$comment": "Invoice",
"properties": {
"documentType": { "enum": ["invoice"] }
},
"required": ["invoiceId", "invoiceDate"]
},
{
"$comment": "CreditNote",
"properties": {
"documentType": { "enum": ["creditNote"] }
},
"required": ["creditNoteId", "creditNoteDate"]
}
]
}
},
"definitions" : {
"general_identifiers" : {
"type": "object",
"required": [
"documentType"
],
"properties": {
"documentType": {
"enum": [
"invoice",
"creditNote"
]
}
}
}
}
}
But with this i get an Error from https://www.jsonschemavalidator.net
Message: JSON is valid against more than one schema from 'oneOf'. Valid schema indexes: 0, 1.
What have I missed?
You're very close. You just need to pull your oneOf up to the top level so you can reference #/properties/general and #/properties/references from the same schema.
Also, you almost always want to use anyOf instead of oneOf. oneOf enforces that one and only one schema in the list validates. When the schemas are mutually exclusive, oneOf is just asking the validator to do unnecessary work.
"anyOf": [
{
"properties": {
"general": {
"properties": {
"documentType": { "enum": ["invoice"] }
}
},
"references": {
"required": ["invoiceId", "invoiceDate"]
}
}
},
{
"properties": {
"general": {
"properties": {
"documentType": { "enum": ["creditNote"] }
}
},
"references": {
"required": ["creditNoteId", "creditNoteDate"]
}
}
}
]
With the help of Jason Desrosiers I finaly found a solution also for my nested json.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"apiVersion",
"apiDate"
],
"anyOf": [
{
"properties": {
"general": {
"properties": {
"documentType": { "enum": ["invoice"] },
"references": {
"required": ["invoiceId", "invoiceDate"]
}
}
}
}
},
{
"properties": {
"general": {
"properties": {
"documentType": { "enum": ["creditNote"] },
"references": {
"required": ["creditNoteId", "creditNoteDate"]
}
}
}
}
}
],
"properties": {
"apiVersion": {
"type": "string",
"description": "The version of the json file"
},
"apiDate": {
"type": "string",
"description": "The date when the json version was published"
},
"general": {
"$ref": "#/definitions/general_identifiers",
"references": {
"type": "object",
"properties": {
"invoiceId": {
"type": "string"
},
"invoiceDate": {
"type": "string"
},
"creditNoteId": {
"type": "string"
},
"creditNoteDate": {
"type": "string"
}
}
}
}
},
"definitions" : {
"general_identifiers" : {
"type": "object",
"required": [
"documentType"
],
"properties": {
"documentType": {
"enum": [
"invoice",
"creditNote"
]
}
}
}
}
}

Issue with JSON.net anyOf schema validation

I'm using the JSON.Net schema validation package and I've come across a very strange issue. I have tracked down the issue to a use of anyOf in the anyObject definition below:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://example.com/schemas/example/1.0/schema.json",
"anyOf": [
{
"$ref": "#/definitions/anyObject"
}
],
"definitions": {
"anyObject": {
"type": "object",
"properties": {
"type": {
"type": "string"
}
},
"required": [
"type"
],
"anyOf": [
{
"if": {
"properties": {
"type": {
"const": "typeA"
}
}
},
"then": {
"$ref": "#/definitions/typeA"
},
"else": false
},
{
"if": {
"properties": {
"type": {
"const": "typeB"
}
}
},
"then": {
"$ref": "#/definitions/typeB"
},
"else": false
}
]
},
"bodyDefinition": {
"oneOf": [
{
"if": {
"properties": {
"$computed": {
"type": "string"
}
},
"required": [
"$computed"
]
},
"then": {
"$ref": "#/definitions/computedBody"
},
"else": {
"$ref": "#/definitions/wildcardBody"
}
},
{
"type": "string"
}
]
},
"wildcardBody": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/bodyDefinition"
}
},
"firstComputedValue": {
"type": "object",
"additionalProperties": false,
"properties": {
"$computed": {
"const": "first"
},
"values": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/definitions/bodyDefinition"
}
}
},
"required": [
"$computed",
"values"
]
},
"computedBody": {
"oneOf": [
{
"if": {
"properties": {
"$computed": {
"const": "first"
}
}
},
"then": {
"$ref": "#/definitions/firstComputedValue"
},
"else": false
}
]
},
"typeA": {
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"type": "string",
"const": "typeA"
},
"body": {
"$ref": "#/definitions/bodyDefinition"
}
},
"required": [
"type"
]
},
"typeB": {
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"type": "string",
"const": "typeB"
},
"body": {
"$ref": "#/definitions/bodyDefinition"
}
},
"required": [
"type"
]
}
}
}
When I test this json:
{
"type": "typeB",
"body":{
"$computed":"first",
"values":[]
}
}
It should be marked as invalid, because values is required to have at least one value. However it is valid. The below JSON should be considered valid, and the above schema does assert that correctly:
{
"type": "typeB",
"body":{
"$computed":"first",
"values":["foo"]
}
}
If I remove typeA from the anyObject definition, then the validation is performed correctly. Below is the schema that validates correctly:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://example.com/schemas/example/1.0/schema.json",
"anyOf": [
{
"$ref": "#/definitions/anyObject"
}
],
"definitions": {
"anyObject": {
"type": "object",
"properties": {
"type": {
"type": "string"
}
},
"required": [
"type"
],
"anyOf": [
{
"if": {
"properties": {
"type": {
"const": "typeB"
}
}
},
"then": {
"$ref": "#/definitions/typeB"
},
"else": false
}
]
},
"bodyDefinition": {
"oneOf": [
{
"if": {
"properties": {
"$computed": {
"type": "string"
}
},
"required": [
"$computed"
]
},
"then": {
"$ref": "#/definitions/computedBody"
},
"else": {
"$ref": "#/definitions/wildcardBody"
}
},
{
"type": "string"
}
]
},
"wildcardBody": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/bodyDefinition"
}
},
"firstComputedValue": {
"type": "object",
"additionalProperties": false,
"properties": {
"$computed": {
"const": "first"
},
"values": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/definitions/bodyDefinition"
}
}
},
"required": [
"$computed",
"values"
]
},
"computedBody": {
"oneOf": [
{
"if": {
"properties": {
"$computed": {
"const": "first"
}
}
},
"then": {
"$ref": "#/definitions/firstComputedValue"
},
"else": false
}
]
},
"typeA": {
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"type": "string",
"const": "typeA"
},
"body": {
"$ref": "#/definitions/bodyDefinition"
}
},
"required": [
"type"
]
},
"typeB": {
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"type": "string",
"const": "typeB"
},
"body": {
"$ref": "#/definitions/bodyDefinition"
}
},
"required": [
"type"
]
}
}
}
Can anyone see if there is an issue with this definition, or is this an issue with the JSON.Net schema package?
This testing was done against the online version of the schema validator at https://www.jsonschemavalidator.net/
I think this is a bug. I'll speak to the library author about it!
To debug, I followed the validation process through the schema, setting $ref or then or else to false... when I reached computedBody, I changed it to the following...
"computedBody": {
"if": {
"properties": {
"$computed": {
"const": "first"
}
}
},
"then": false,
"else": false
}
Validation still comes back positive, which shouldn't be possible. I proved it reaches computedBody by setting that subschema to false and seeing validation come back negative.
(the oneOf wrapping the subschema of computedBody isn't needed. if is valid at the schema level just fine).