validate one of the Array object in json schema conditionally - json

In below json is valid only if type is mobile and endDate is empty/null, else validation need to fail. Here type can have any values but my validation only on mobile type.
Valid json:
{
"contacts": [
{
"type": "mobile",
"endDate": "",
"number": "1122334455"
},
{
"type": "home",
"endDate": "",
"number": "1111122222"
},
{
"type": "mobile",
"endDate": "12-Jan-2017",
"number": "1234567890"
},
]
}
Invalid json: (since contacts don't have a valid mobile number)
{
"contacts": [
{
"type": "mobile",
"endDate": "12-Jan-2021",
"number": "1122334455"
},
{
"type": "home",
"endDate": "",
"number": "1111122222"
},
{
"type": "mobile",
"endDate": "12-Jan-2017",
"number": "1234567890"
},
]
}
Schema which i tried
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "object",
"properties": {
"contacts": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"endDate": {
"type": [
"string",
"null"
]
},
"number": {
"type": "string"
}
},
"anyOf": [
{
"if": {
"properties": {
"type": {
"const": "mobile"
}
}
},
"then": {
"properties": {
"endDate": {
"maxLength": 0
}
}
}
}
]
}
}
}
}
could anyone provide me a right schema for the above json, attaching the example code here, this is valid json but getting error. Invalid json example is here why because type mobile don't have empty endDate.
Thanks in advance.

I found the answer some how, we need not to use anyOf or oneOf. The right one is contains. place of the contains also a mater. working example is here
Here is correct schema
{
"type": "object",
"properties": {
"contacts": {
"type": "array",
"minItems": 1,
"contains": {
"type": "object",
"properties": {
"type": {
"const": "mobile"
},
"endDate": {
"type" : ["string", "null"],
"maxLength": 0
}
},
"required": ["type"]
},
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"endDate": {
"type": [
"string",
"null"
]
},
"number": {
"type": "string"
}
}
}
}
}
}

Your schema defines properties > data but your instance data uses contacts rather than data. Changing either fixes your problem. You have otherwise done this correctly.
(If you only have one type to check, you do not need to wrap your if/then schema in an anyOf.)

Related

Issues creating Json schema with enums

I am new to JSON and I am trying to create a JSON schema for a JSON object. There are tools online that can easily create JSON schema from a JSON object but my JSON seems to be complicated and needs some tweaking, but I am not sure how
example json
{
"iD": 123456789,
"balance": {
"currency": "Pounds",
"balance": 260.25,
"date": "2022-03-03"
},
"Permissions": {
"granted": [
"canLoginWithPassword",
"canGame",
"canWithdraw"
],
"failedConditions": [
{
"name": "we move",
"description": "Deposit Block",
"details": {
"reason": "Deposit Block"
},
"denied": [
"canDeposit"
]
}
]
}
The problem I am having is not being able to represent the granted section and denied section with enumerations. The granted and denied sections can have one or all of the privileges
Privileges
"canLoginWithPassword",
"canGame",
"canWithdraw",
"canDeposit"
From reading your question, I think the items, uniqueItems and enum keywords are the way to go for your schema:
{
"type": "object",
"$schema": "http://json-schema.org/draft-06/schema#",
"description": "JSON schema generated with JSONBuddy https://www.json-buddy.com",
"properties": {
"Permissions": {
"$ref": "#/definitions/Permissions"
},
"balance": {
"$ref": "#/definitions/balance"
},
"iD": {
"type": "integer"
}
},
"definitions": {
"privileges": {
"type": "array",
"uniqueItems": true,
"items": {
"type": "string",
"enum": [
"canLoginWithPassword",
"canGame",
"canWithdraw",
"canDeposit"
]
}
},
"details": {
"type": "object",
"properties": {
"reason": {
"type": "string"
}
}
},
"Permissions": {
"type": "object",
"properties": {
"failedConditions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"denied": {
"$ref": "#/definitions/privileges"
},
"description": {
"type": "string"
},
"details": {
"$ref": "#/definitions/details"
},
"name": {
"type": "string"
}
}
}
},
"granted": {
"$ref": "#/definitions/privileges"
}
}
},
"balance": {
"type": "object",
"properties": {
"balance": {
"type": "number"
},
"currency": {
"type": "string"
},
"date": {
"type": "string"
}
}
}
}
}

Validate JSON Schema based on "type" property

I have the following JSON which validates against my current schema:
{
"information": [
{
"value": "closed",
"type": "power"
},
{
"value": "on",
"type": "door"
}
]
}
However, I want this validation to fail (since open/closed should relate to door, and on/off should only relate to power.
How can I tie the two together?
My current working schema:
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"definitions": {
"Type": {
"type": "string",
"enum": ["power", "door"]
},
"Power": {
"type": "string",
"enum": ["on", "off"]
},
"Door": {
"type": "string",
"enum": ["open", "closed"]
},
"InformationField": {
"type": "object",
"required": [ "type", "value" ],
"properties": {
"label": { "type": "string" },
"value": {
"anyOf": [
{ "$ref": "#/definitions/Power"},
{ "$ref": "#/definitions/Door"}
]
},
"type": { "$ref": "#/definitions/Type" }
}
},
"Information": {
"type": "array",
"items": { "$ref": "#/definitions/InformationField" }
},
},
"properties": {
"information": { "$ref": "#/definitions/Information" }
},
}
I have a lot of types, so I want to do this in the cleanest way possible.
Here is one of my attempts, which fails to validate correctly:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"definitions": {
"Type": {
"type": "string",
"enum": ["power", "door"]
},
"Power": {
"type": "string",
"enum": ["on", "off"]
},
"Door": {
"type": "string",
"enum": ["open", "closed"]
},
"InformationField": {
"type": "object",
"required": [ "label", "value" ],
"properties": {
"label": { "type": "string" },
"type": { "$ref": "#/definitions/Type" }
},
"anyOf": [
{
"if": {
"properties": { "type": { "const": "power" } }
},
"then": {
"properties": { "value": { "$ref": "#/definitions/Power" } }
}
},
{
"if": {
"properties": { "type": { "const": "door" } }
},
"then": {
"properties": { "value": { "$ref": "#/definitions/Door" } }
}
}
]
},
"Information": {
"type": "array",
"items": { "$ref": "#/definitions/InformationField" }
},
},
"properties": {
"information": { "$ref": "#/definitions/Information" }
},
}
(Validates, even though it shouldn't ...)
Changed anyOf to allOf in my attempt, which fixes validation.
My reasoning as to why this works:
From the anyOf JSON schema spec:
5.5.4.2. Conditions for successful validation
An instance validates successfully against this keyword if it validates successfully against at least one schema defined by this keyword's value.
If my first if condition is false, then it doesn't evaluate the then, and is therefore valid.
Using allOf evaluates every condition's validation (not the condition itself).

Assistance needed to create JSON schema

As I am new to JSON schema creation, I have learnt the basics of JSON schema and now I am trying to create JSON schema for the below mentioned set of data,
{
"Result": [
{
"ResourceName": "Appointment",
"Sequence": "1",
"Data": {
"AppointmentID": "A1234",
"PatientName": "Test Patient",
"ClinicName": "Test Clinic"
}
},
{
"ResourceName": "EpisodeofCare",
"Sequence": "2",
"Data": {
"EpisodeID": "EP1234",
"LocationId": "L1234",
"AppointmentId": "A1234",
"TransactionStatus": "2",
"OPNumber": "OP523367"
}
},
{
"ResourceName": "Encounter",
"Sequence": "3",
"Data": {
"EncounterID": "E1234",
"PatientID": "P1234"
}
}
]
}
Can anybody please help me to create JSON schema for this kind of data set.
Thanks in advance for helping me out on this.
Below is the JSON schema i have drafted
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"ResultType": {
"type": "object",
"properties": {
"ResourceName": {
"type": "string"
},
"Sequence": {
"type": "string"
},
"Data": {
"type": "object",
"anyOf": [
{
"$ref": "#/definitions/Appointment"
},
{
"$ref": "#/definitions/EpisodeofCare"
},
{
"$ref": "#/definitions/Encounter"
}
]
}
}
},
"Appointment": {
"type": "object",
"properties": {
"AppointmentID": {
"type": "boolean"
},
"PatientName": {
"type": "string"
},
"ClinicName": {
"type": "string"
}
}
},
"EpisodeofCare": {
"type": "object",
"properties": {
"EpisodeID": {
"type": "string"
},
"LocationId": {
"type": "string"
},
"AppointmentId": {
"type": "string"
},
"TransactionStatus": {
"type": "string"
},
"OPNumber": {
"type": "string"
}
}
},
"Encounter": {
"type": "object",
"properties": {
"EncounterID": {
"type": "string"
},
"PatientID": {
"type": "string"
}
}
}
},
"type": "object",
"properties": {
"Result": {
"type": "array",
"$ref": "#/definitions/ResultType"
}
}
}
Throw it in a schema validator like this one.
You will see errors in your schemas definition and in validation.
First of all check that your schema is valid json (the validator might help):
[
{
{"$ref": "#/definitions/Appointment"} <- unnecessary and invalid braces
},
{
{"$ref": "#/definitions/EpisodeofCare"} <- unnecessary and invalid braces
},
{
{"$ref": "#/definitions/Encounter"} <- unnecessary and invalid braces
}
]
Within the Result property you want each item of the array. You should use the item property instead of ref. As taken from the specification:
The value of "items" MUST be either a valid JSON Schema or an array of valid JSON Schemas.
[...]
If "items" is a schema, validation succeeds if all elements in the array successfully validate against that schema.
Thus you should use
{
"type": "object",
"properties": {
"Result": {
"type": "array",
"items": {
"$ref": "#/definitions/ResultType"
}
}
}
}
Since the schema should be as specific as possible you should constraint the content of the ResourceName properties to the actual data layout.
Use the const property to define a constant value for the ResourceName for each type of Resource. Take for example the Appointment type, which would change to:
"Appointment": {
"type": "object",
"properties": {
"ResourceName": {
"const": "Appointment"
},
"Data": {
"type": "object",
"properties": {
"AppointmentID": {
"type": "string"
},
"PatientName": {
"type": "string"
},
"ClinicName": {
"type": "string"
}
}
}
}
I also changed the type of AppointmentID to string.
Then your ResultType needs to be adjusted. Before you used anyOf to validate the data, now you need it to validate the complete object while still maintaining the overall structure:
"ResultType": {
"allOf": [
{
"type": "object",
"properties": {
"ResourceName": {
"type": "string"
},
"Sequence": {
"type": "string"
},
"Data": true
}
},
{
"anyOf": [
{
"$ref": "#/definitions/Appointment"
},
{
"$ref": "#/definitions/EpisodeofCare"
},
{
"$ref": "#/definitions/Encounter"
}
]
}
]
}
The allOf propertiy implies a and condition. The overall structure (the object) does not care what value ResourceName has or how Data is structured. The specific implementations (the anyOf part) take care of that.
Some final improvements. Use the required property to define the names of properties that must exist in your JSON file. Also add "additionalProperties": false to ensure that only the specified properties are used. And finally constraint your sequence values to be string representations of positive intergers by adding "pattern": "[1-9]\\d*".
Final result:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"ResultType": {
"allOf": [
{
"type": "object",
"required": [
"ResourceName",
"Sequence",
"Data"
],
"additionalProperties": false,
"properties": {
"ResourceName": {
"type": "string"
},
"Sequence": {
"type": "string",
"pattern": "[1-9]\\d*"
},
"Data": true
}
},
{
"anyOf": [
{
"$ref": "#/definitions/Appointment"
},
{
"$ref": "#/definitions/EpisodeofCare"
},
{
"$ref": "#/definitions/Encounter"
}
]
}
]
},
"Appointment": {
"type": "object",
"properties": {
"ResourceName": {
"const": "Appointment"
},
"Data": {
"type": "object",
"required": [
"AppointmentID",
"PatientName",
"ClinicName"
],
"additionalProperties": false,
"properties": {
"AppointmentID": {
"type": "string"
},
"PatientName": {
"type": "string"
},
"ClinicName": {
"type": "string"
}
}
}
}
},
"EpisodeofCare": {
"type": "object",
"properties": {
"ResourceName": {
"const": "EpisodeofCare"
},
"Data": {
"type": "object",
"required": [
"EpisodeID",
"LocationId",
"AppointmentId",
"TransactionStatus",
"OPNumber"
],
"additionalProperties": false,
"properties": {
"EpisodeID": {
"type": "string"
},
"LocationId": {
"type": "string"
},
"AppointmentId": {
"type": "string"
},
"TransactionStatus": {
"type": "string"
},
"OPNumber": {
"type": "string"
}
}
}
}
},
"Encounter": {
"type": "object",
"properties": {
"ResourceName": {
"const": "Encounter"
},
"Data": {
"type": "object",
"required": [
"EncounterID",
"PatientID"
],
"additionalProperties": false,
"properties": {
"EncounterID": {
"type": "string"
},
"PatientID": {
"type": "string"
}
}
}
}
}
},
"type": "object",
"required": [
"Result"
],
"additionalProperties": false,
"properties": {
"Result": {
"type": "array",
"items": {
"$ref": "#/definitions/ResultType"
}
}
}
}
you can create json schema by online tools which generates json schema from the json
like: https://www.liquid-technologies.com/online-json-to-schema-converter , it also checks if json is valid or not.
Json Schema that you need:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"Result": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"ResourceName": {
"type": "string"
},
"Sequence": {
"type": "string"
},
"Data": {
"type": "object",
"properties": {
"AppointmentID": {
"type": "string"
},
"PatientName": {
"type": "string"
},
"ClinicName": {
"type": "string"
}
},
"required": [
"AppointmentID",
"PatientName",
"ClinicName"
]
}
},
"required": [
"ResourceName",
"Sequence",
"Data"
]
},
{
"type": "object",
"properties": {
"ResourceName": {
"type": "string"
},
"Sequence": {
"type": "string"
},
"Data": {
"type": "object",
"properties": {
"EpisodeID": {
"type": "string"
},
"LocationId": {
"type": "string"
},
"AppointmentId": {
"type": "string"
},
"TransactionStatus": {
"type": "string"
},
"OPNumber": {
"type": "string"
}
},
"required": [
"EpisodeID",
"LocationId",
"AppointmentId",
"TransactionStatus",
"OPNumber"
]
}
},
"required": [
"ResourceName",
"Sequence",
"Data"
]
},
{
"type": "object",
"properties": {
"ResourceName": {
"type": "string"
},
"Sequence": {
"type": "string"
},
"Data": {
"type": "object",
"properties": {
"EncounterID": {
"type": "string"
},
"PatientID": {
"type": "string"
}
},
"required": [
"EncounterID",
"PatientID"
]
}
},
"required": [
"ResourceName",
"Sequence",
"Data"
]
}
]
}
},
"required": [
"Result"
]
}

JSON-Schema: Conditional dependency (by value)

This is the simplified JSON-Schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "user",
"type": "object",
"properties": {
"account": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["COMPANY", "PERSON"]
}
},
"required": ["type"]
},
"person": {
"type": "object",
"properties": {
"firstName": { "type": "string" },
"lastName": { "type": "string" }
},
"required": ["firstName", "lastName"]
},
"company": {
"type": "object",
"properties": {
"name": { "type": "string" },
"taxNumber": { "type": "string" }
}
}
},
"required": ["account", "person"]
}
What I want to achieve is:
If account.type is set to "COMPANY":
company object and its properties should be required.
If account.type isset to "PERSON":
company object should be optional.
But if company object is present, company.name and company.taxNumber should be required.
This can be achieved by defining two long sub-schemas under a oneOf but that would mean too many duplicates and a complex schema, since account and company has many more properties than this simplified version.
AFAIK, the only way to define a specific value in a schema is by using the enum keyword with a single item. I tried this with the dependencies keyword but didn't help.
Can you think of a way without altering the structure of the data object?
You can express this requirement using switch keyword from JSON-schema v5/6 proposals that is supported in Ajv (I am the author).
Under Draft-07, you can do it by conditionally applying schema requirements:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"id": "user",
"type": "object",
"properties": {
"account": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["COMPANY", "PERSON"]
}
},
"required": ["type"]
},
"person": {
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
}
},
"required": ["firstName", "lastName"]
},
"company": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"taxNumber": {
"type": "string"
}
}
}
},
"if": {
"properties": {
"account": {
"const": {
"type": "COMPANY"
}
}
}
},
"then": {
"required": ["account", "person", "company"]
},
"else": {
"required": ["account", "person"]
}
}
Here's a working example that validates against it:
{
"account": {
"type": "COMPANY"
},
"person": {
"firstName": "John",
"lastName": "Doe"
},
"company": {
"name": "XYZ Corp"
}
}

Can maxItems/minItems be used with a $ref in a JSON schema

Given a JSON schema with the following in the definitions section:
"phoneNumber": {
"type": "object",
"properties": {
"countryCode": {
"type": "number"
},
"areaCode": {
"type": "number"
},
"number": {
"type": "number"
},
"extension": {
"type": "number"
},
"service": {
"type": "string",
"enum": ["Voice", "Fax", "Data"]
},
"class": {
"type": "string",
"enum": ["Switchboard", "Direct", "PA", "Mobile"]
}
}
}
If I want to include phoneNumber elsewhere using a $ref and want the JSON to validate if it contains multiple occurrences of phoneNumber, can I use maxItems/minItems:
"person": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"phoneNumber": {
"$ref": "#/definitions/phoneNumber"
//can I use maxItems/minItems here?
}
}
}
Can I use maxItems and minItems here, or would I have to do something like this below for it to validate:
"phoneNumber": {
"allOf": { "$ref": "#/definitions/phoneNumber" },
"maxItems": 4
}
$ref must stand alone. The option you identified using allOf is the best way to do it.
Any members other than "$ref" in a JSON Reference object SHALL be ignored.
https://datatracker.ietf.org/doc/html/draft-pbryan-zyp-json-ref-03#section-3