JSON schema for object with either A and B or C required properties - jsonschema

I have two possible JSON objects for one request:
{
"from": "string",
"to": "string",
"text": "string"
}
or
{
"number": "integer",
"text": "string"
}
In both cases "text" property is optional. Other properties are required (either "number, or both "from" and "to").
What will be the correct JSON schema to validate this?

Here is another solution that I think is a bit more clear. The dependencies clause ensures that "from" and "to" always come as a pair. Then the oneOf clause can be really simple and avoid the not-required boilerplate.
{
"type": "object",
"properties": {
"from": { "type": "string" },
"to": { "type": "string" },
"number": { "type": "integer" },
"text": { "type": "string" }
},
"dependencies": {
"from": ["to"],
"to": ["from"]
},
"oneOf": [
{ "required": ["from"] },
{ "required": ["number"] }
]
}

Finally managed to build the correct scheme.
{
"definitions": {
"interval": {
"type": "object",
"properties": {
"from": {
"type": "string"
},
"to": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": ["from", "to"],
"not": {
"required": ["number"]
}
},
"top": {
"type": "object",
"properties": {
"number": {
"type": "integer"
},
"text": {
"type": "string"
}
},
"required": ["number"],
"allOf": [
{
"not": {
"required": ["from"]
}
},
{
"not": {
"required": ["to"]
}
}
]
}
},
"type": "object",
"oneOf": [
{"$ref": "#/definitions/interval"},
{"$ref": "#/definitions/top"}
]
}

Related

Easy way to group custom defined types to use for reference?

How to reference types by group instead of doing it individually?
For example here, I want "sum" and "subtract" types to accept types that are "number"-related types only("number", "sum", "subtract"). While "concatenate" can accept both "number" and "string"-related types. Including their selves. This will be really helpful for me if in case I added more types and want them to be automatically included.
"sum": {
"title": "sum",
"type": "object",
"properties": {
"values": {
"type": "array",
"items": {
"anyOf": [
{
"type": "number"
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
},
"required": [
"values"
]
},
"subtract": {
"title": "subtract",
"type": "object",
"properties": {
"minuend": {
"type": "number"
},
"subtrahend": {
"type": "number"
}
},
"required": [
"minuend",
"subtrahend"
]
},
"concatenate": {
"title": "concatenate",
"type": "object",
"properties": {
"strings": {
"type": "array",
"items": {
"anyOf": [
{
"type": "number"
},
{
"type": "string"
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
},
"required": [
"strings"
]
}
}
Here's the full schema:
{
"definitions": {
"sum": {
"title": "sum",
"type": "object",
"properties": {
"values": {
"type": "array",
"items": {
"anyOf": [
{
"type": "number"
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
},
"required": [
"values"
]
},
"subtract": {
"title": "subtract",
"type": "object",
"properties": {
"minuend": {
"type": "number"
},
"subtrahend": {
"type": "number"
}
},
"required": [
"minuend",
"subtrahend"
]
},
"concatenate": {
"title": "concatenate",
"type": "object",
"properties": {
"strings": {
"type": "array",
"items": {
"anyOf": [
{
"type": "number"
},
{
"type": "string"
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
},
"required": [
"strings"
]
}
},
"title": "Step",
"type": "object",
"properties": {
"value": {
"oneOf": [
{
"type": "number",
"minimum": 0
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
}

json schema validation: a string field required if another array field contains specific value

cant build validation for simple case:
if sources field contains "OTHER" in values then "sourceOtherDescription" must be required.
Shall pass validation
{
"sources": ["RENTS"]
}
{
"sources": ["RENTS", "OTHER"],
"sourceOtherDescription": "other income"
}
This should not pass validation since sources contains "OTHER"
{
"sources": ["RENTS", "OTHER"]
}
The schema that I was able to produce. Does not really work
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "money-sources",
"title": "Money Sources",
"description": "Money Sources definitions",
"type": "object",
"required": ["sources"],
"properties": {
"sources": {
"type": "array",
"items": {
"type": "string",
"enum": [
"RENTS",
"MEMBER_FEES",
"PROFIT",
"SALES_SECURITIES",
"INTERNAL_GROUP_TRANSFERS",
"OTHER"
]
},
"uniqueItems": true
},
"sourceOtherDescription": { "type": "string", "minLength": 3}
},
"additionalProperties": false,
"oneOf": [
{
"properties": {
"sources": {
"type": "array",
"contains": {"const": "OTHER"}
}
},
"required": ["sourceOtherDescription"]
},
{
"properties": {
"sources": {
"type": "array",
"contains": {
"enum": [
"RENTS",
"MEMBER_FEES",
"PROFIT",
"SALES_SECURITIES"
]
}
}
}
}
, false
]
}
Using if-then it works for me this way:
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "money-sources",
"title": "Money Sources",
"description": "Money Sources definitions",
"type": "object",
"required": [ "sources" ],
"properties": {
"sources": {
"type": "array",
"items": {
"type": "string",
"enum": [
"RENTS",
"MEMBER_FEES",
"PROFIT",
"SALES_SECURITIES",
"INTERNAL_GROUP_TRANSFERS",
"OTHER"
]
},
"uniqueItems": true
},
"sourceOtherDescription": {
"type": "string",
"minLength": 3
}
},
"additionalProperties": false,
"if": {
"properties": {
"sources": {
"type": "array",
"contains": {
"const": "OTHER"
}
}
}
},
"then": {
"required": [ "sourceOtherDescription" ]
}
}

Json Schema dependency on outer field

Given a json structure like this
{
"name": "John Doe",
"billing_address": "123 main st",
"payment_details":{"credit_card": 55555555}
}
I need to make 'billing_address' a required field if payment_details.credit_card exists.
I've started from this example that achieves so if both fields were at the same level of nesting
{
"type": "object",
"properties": {
"name": { "type": "string" },
"credit_card": { "type": "number" }
},
"required": ["name"],
"dependencies": {
"credit_card": {
"properties": {
"billing_address": { "type": "string" }
},
"required": ["billing_address"]
}
}
}
However, what is the syntax to require this field that is one level above the dependant field?
That's too complicated for 'dependencies', so we go back to an if/then/else clause for that. We place these keywords at the top level, where the required field needs to be. In pseudocode: "if there is a payment_details property present, and it has a credit_card property, then require billing_address."
{
...,
"if": {
"type": "object",
"required": [ "payment_details" ],
"properties": {
"payment_details": {
"type": "object",
"required": [ "credit_card" ]
}
},
"then": {
"type": "object",
"required": [ "billing_address" ]
}
}
Note that the "type" and "required" keywords are required here -- as data of different types (for example an array) will cause object-specific keywords like "properties" and "required" to always evaluate to true.
thanks Ether! this worked, but only after adding the credit card as a property, in the if section.
the full if statement looks like this
"if": {
"type": "object",
"required": ["payment_details"],
"properties": {
"payment_details": {
"type": "object",
"properties": {
"credit_card": {}
},
"required": ["credit_card"]
}}},
the full json schema looks like this
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"billing_address": {
"type": "string"
},
"payment_details": {
"type": "object",
"properties": {
"credit_card": {
"type": "integer"
}
}
}
}
,
"if": {
"type": "object",
"required": ["payment_details"],
"properties": {
"payment_details": {
"type": "object",
"properties": {
"credit_card": {}
},
"required": ["credit_card"]
}}},
"then": {
"type": "object",
"required": [ "billing_address" ]
}
}

jsonschema with custom property

I want to define a JsonSchema with a customProperty in it, this property follows some rules, so in order to validate those, I need to define a JsonSchema that will validate it.
So far I've managed to describe it properly, but it only works for the first level of attributes, and I want it to be recursive...
From my comprehension it should work, I probably made a mistake I'm unable to see and at this point I don't know if it's a bug, impossible, or stupid...
I believe redefining every type should be a possibility , but obviously I'd rather not.
Here is an example of the Json I want to validate
{
"title": "TheObject",
"type": "object",
"properties": {
"aString": {
"type": "string",
"myCustomProperty": {}
},
"anObjet": {
"type": "object",
"myCustomProperty": {},
"properties": {
"anotherObject": {
"type": "object",
"myCustomProperty": {}, //if this line is removed it still validates which I don't want
"properties": {}
}
}
}
}
}
and here is the JsonSchema I've done so far:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"title": {"type": "string"},
"type": {"type": "string","enum": ["object"]},
"properties": {
"type": "object",
"patternProperties": {
".*": {
"$ref": "#/definitions/Field"
}
}
}
},
"definitions": {
"Field": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"myCustomProperty": {
"$ref": "#/definitions/myCustomProperty"
},
"patternProperties": {
"^(?!myCustomProperty).*": {
"$ref": "#/definitions/Field"
}
}
},
"required": [
"type",
"myCustomProperty"
]
},
"myCustomProperty": {
//Some rules
}
}
}
I found a solution, I wasn't far from what I wanted after all.
In my definition of "Field", I am describing an object that defines an object, and I was missing the "properties" field. In which I had to put my recursive reference.
The right jsonSchema is the following :
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"title": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"object"
]
},
"properties": {
"type": "object",
"patternProperties": {
".*": {
"$ref": "#/definitions/Field"
}
}
}
},
"definitions": {
"Field": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"myCustomProperty": {
"$ref": "#/definitions/myCustomProperty"
},
"properties": { <==================== here
"type": "object",
"patternProperties": {
".*": {
"$ref": "#/definitions/Field"
}
}
}
},
"required": [
"type",
"myCustomProperty"
]
},
"myCustomProperty": {/*rules*/}
}
}
So far it works as expected, but if someone has a more elegant suggestion please share!

Using a json schema in multiple layouts

I'm helping to build an interface that works with Json Schema, and I have a question about interface generation based on that schema. There are two display types - one for internal users and one for external users. Both are dealing with the same data, but the external users should see a smaller subset of fields than the internal users.
For example, here is one schema, it defines an obituary:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "",
"type": "object",
"required": [
"id",
"deceased"
],
"properties": {
"id": { "type": "string" },
"account": {
"type": "object",
"required": [
"name"
],
"properties": {
"id": { "type": "number" },
"name": { "type": "string" },
"website": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "string",
"maxLength": 0
}
]
},
"email": {
"anyOf": [
{
"type": "string",
"format": "email"
},
{
"type": "string",
"maxLength": 0
}
]
},
"address": {
"type": "object",
"properties": {
"address1": { "type": "string" },
"address2": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"postalCode": { "type": "string" },
"country": { "type": "string" }
}
},
"phoneNumber": {
"anyOf": [
{
"type": "string",
"format": "phone"
},
{
"type": "string",
"maxLength": 0
}
]
},
"faxNumber": {
"anyOf": [
{
"type": "string",
"format": "phone"
},
{
"type": "string",
"maxLength": 0
}
]
},
"type": { "type": "string" }
}
},
"deceased": {
"type": "object",
"required": [
"fullName"
],
"properties": {
"fullName": { "type": "string" },
"prefix": { "type": "string" },
"firstName": { "type": "string" },
"middleName": { "type": "string" },
"nickName": { "type": "string" },
"lastName1": { "type": "string" },
"lastName2": { "type": "string" },
"maidenName": { "type": "string" },
"suffix": { "type": "string" }
}
},
"description": { "type": "string" },
"photos": {
"type": "array",
"items": { "type": "string" }
}
}
}
Internal users would be able to access all the fields, but external users shouldn't be able to read/write the account fields.
Should I make a second schema for the external users, or is there a way to indicate different display levels or public/private on each field?
You cannot restrict acess to the fields defined in a schema, but you can have 2 schema files, one defining the "public" fields, and the other one defining the restricted fields plus including the restricted fields.
So
public-schema.json:
{
"properties" : {
"id" : ...
}
}
restricted-schema.json:
{
"allOf" : [
{
"$ref" : "./public-schema.json"
},
{
"properties" : {
"account": ...
}
}
]
}