JSON schema validation with Map of string with Enum constraints - json

Requesting help with JSON Schema validation, below is sample JSON and Schema. I am trying to figure out how to specify "ppd" schema rule specifically "cfg" is a map of String, String and need to further restrict the entries of the key and value in this map by Enum definition i.e. allowed values for "inputDateTimeFormat" is a valid date time format so rule should encode if key is "inputDateTimeFormat" then allowed value is a pattern matching date time format and similarly if key is "valuemapping" then allowed values is pattern matching k=v (example below).
Could you please suggest a way to achieve this?
JSON Sample -
{
"sm": [
{
"mid": "id-1",
"ppd": [
{
"name": "cc-1",
"cfg": {
"columns": "v-1",
"valueMapping": "B=01;S=02"
}
},
{
"name": "cc-2",
"cfg": {
"columns": "v-2",
"inputDateTimeFormat": "ddMMMyyyy_HH:mm:ss.SSSSSS",
"outputDateTimeFormat": "yyyy-MM-dd'T'HH:mm:Ss.SSSZ"
}
},
{
"name": "cc-3",
"cfg": {
"columns": "v-3;v-4",
"markers": "d=01"
}
}
]
}
]
}
JSON Schema :
{
"type": "object",
"$schema": "http://json-schema.org/draft-06/schema",
"id": "source-mappings-schema",
"required": true,
"properties": {
"sm": {
"type": "array",
"id": "source-mappings-schema/sm",
"required": true,
"items": {
"type": "object",
"id": "source-mappings-schema/sm/0",
"required": true,
"properties": {
"mappingId": {
"type": "string",
"id": "source-mappings-schema/sm/0/mappingId",
"required": true
},
"ppd": {
"type": "array",
"id": "source-mappings-schema/sm/0/ppd",
"required": true,
"items": {
"type": "object",
"id": "source-mappings-schema/sm/0/ppd/0",
"required": true,
"properties": {
"name": {
"type": "string",
"id": "source-mappings-schema/sm/0/ppd/0/name",
"required": true
},
"cfg": {
"type": "array",
"id": "source-mappings-schema/sm/0/ppd/0/cfg",
"required": true,
"items": {
"type": "string"
}
}
}
}
}
}
}
}
}
}

To start with your schema contains a few issue.
The $schema tag is wrong, it should be
"$schema": "http://json-schema.org/draft-06/schema#",
The 'required' property is supposed to be an array of property names that are required (not a bool), so you need to apply this at the level above.
Finally the validation of cfg. By specifying a schema for 'additionalProperties' you can provide validation rules for all unspecified key values (you said it was a map of strings, so I've set it to string, but you could also add other rules here like max length etc).
For the keys you know about you can add a property for each of them with the approrate validation rules (the rules i've added demonstrate the concept and will need tweaking for your use).
"cfg": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"properties": {
"inputDateTimeFormat": {
"type": "string",
"format": "date-time"
},
"valuemapping": {
"type": "string",
"pattern": "[a-z]\\=[a-z]"
}
}
}

Related

Conditional References in a JSON Schema

I want to write a single file JSON schema definition with several sub schemas that I can combine, depending on the payload.
The following schema validates, that my schema is working with my sample JSON response. (The response object has a wrong type for payload.role to make sure the schema catches this mistake!)
For clarity, I reduce it on the most important parts. A full working example can be found here: https://www.jsonschemavalidator.net/s/3KAaXjtg
Schema
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "http://example.com/baseSchema.json",
"type": "object",
"required": [
"payload"
],
"properties": {
"payload": {
"$id": "#/properties/payload",
"type": "object",
// reference the right schema depending on the payload child key
// if `payload.user` reference `userSchema.json`
// if `payload.users` reference `usersSchema.json`
// if `payload.*` reference `*Schema.json`
"$ref": "userSchema.json"
}
},
"definitions": {
"user": {
"$id": "http://example.com/userSchema.json",
"type": "object",
"required": [
"user"
],
"properties": {
"user": {
"type": "object",
"$ref": "userProperties.json"
}
}
},
"users": {
"$id": "http://example.com/usersSchema.json",
"type": "object",
"required": [
"users"
],
"properties": {
"users": {
"type": "array",
"items": {
"$ref": "userProperties.json"
}
}
}
},
"userProperties": {
"$id": "http://example.com/userProperties.json",
"type": "object",
"properties": {
"firstName": {
"$id": "#/properties/payload/properties/user/properties/firstName",
"type": "string"
}
}
}
}
}
Response
{
"status": {
"code": 200,
"description": "User retrieved successfully."
},
"payload": {
"user": {
"firstName": "Joe",
"lastName": "Doe",
"role": "3", // for testing reasons, this is the wrong type!
"email": "doe#example.com",
"customerID": "",
"projects": [
"AIXG5mEg6QLl9rhVSE6m",
"Bs1bHiOIqKclwwis3CNf",
"NC2OUGVZXU35FA7iwRn4"
],
"status": "Status",
"id": "c555BSZnKLdHSRYqrU5hqiQo733j13"
}
}
}
So I've got a baseSchema.json that matches this response:
{
"status": {},
"payload": {}
}
payload gets extended by a certain key like payload.user = {} or payload.foo = {} and depending on that key, I want to extend schema with one of my definitions.
The following part only works for the key user:
"properties": {
"payload": {
"$id": "#/properties/payload",
"type": "object",
// reference the right schema depending on the payload child key
// if `payload.user` reference `userSchema.json`
// if `payload.users` reference `usersSchema.json`
// if `payload.*` reference `*Schema.json`
"$ref": "userSchema.json"
}
},
I failed to setup any conditions (with allOf, if, else), that would reference the correct sub-schema, based on the payload key.
Any hints and help to solve that is appreciated.
Schema and link to demo at the end... Let's look at how we got there...
In JSON Schema draft-07 and previous, you can't use $ref alongside other keywords. Other keywords are ignored. (In your schema http://example.com/userSchema.json you had type next to $ref). Fortunatly this didn't cause you any problems as you declare the type in the referenced schema. (You CAN do this with 2019-09 or above.)
The values for the keywords if, then, and else are schemas.
For the then subschema to be applied to your instance location, the if schema must come back as valid. If it fails, the else subschema value will be applied.
Our if condition checks for the presense of a specific key.
If the key exists, THEN apply the schema which references the correct schema.
Because you want the conditions to be mutually exclusive, you need to wrap the multiple conditions in a oneOf, and add else: false to the conditional checks. false as a schema makes validation fail.
Let me know if you want any further clarification on any of the above.
Demo: https://jsonschema.dev/s/HLniL
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "http://example.com/baseSchema.json",
"type": "object",
"required": [
"payload"
],
"properties": {
"payload": {
"$id": "#/properties/payload",
"type": "object",
"oneOf": [
{
"if": {
"required": [
"user"
]
},
"then": {
"$ref": "userSchema.json"
},
"else": false
},
{
"if": {
"required": [
"users"
]
},
"then": {
"$ref": "usersSchema.json"
},
"else": false
}
]
}
},
"definitions": {
"user": {
"$id": "http://example.com/userSchema.json",
"type": "object",
"required": [
"user"
],
"properties": {
"user": {
"$ref": "userProperties.json"
}
}
},
"users": {
"$id": "http://example.com/usersSchema.json",
"type": "object",
"required": [
"users"
],
"properties": {
"users": {
"type": "array",
"items": {
"$ref": "userProperties.json"
}
}
}
},
"userProperties": {
"$id": "http://example.com/userProperties.json",
"type": "object",
"properties": {
"firstName": {
"type": "string"
}
}
}
}
}

JSON Schema for child objects with different set of keys

I have JSON data of which is an array of data like
[
{
"type": "background_color",
"data": {
"backgroundColor": "F9192D"
}
},
{
"type": "banner_images",
"data": {
"images": [
{
"url": "https://example.com/abc.jpg",
"id": 3085
},
{
"url": "https://example.com/zyx.jpg",
"id": 3086
}
]
}
},
{
"type": "description_box",
"data": {
"text": "Hello 56787"
}
}
]
The data is an array of object which has two keys type and data. The type and keys of the data will be defined by the type of data it has.
Like for background_color type, the data should have backgroundColor property, while for banner_images, data should have images which is an array of other properties.
Till now, What I have done is
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"title": "category schema",
"description": "Used to validate data of category",
"examples": [],
"required": [],
"items": {
"type": "object",
"required": [
"type",
"data"
],
"properties": {
"type": {
"type": "string",
"enum": ["background_color", "banner_images", "description_box"]
},
"data": {
"type": "object" // How to define data property here for each use case
}
}
}
}
I'm not getting how to define the data property for each use case?
You can use if/then/else blocks to define conditional constraints.
The values of if and then are schemas. If the if schema is valid, then the then schema is applied, otherwise, the allOf subschema (allOf[0] in this example) would pass validation.
There are a few different ways to do this, but this is clean when you don't have any additional or special requirements. Please come back if you do =]
In this example, I've added banner_images...
You can test it working here.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"title": "category schema",
"description": "Used to validate data of category",
"items": {
"type": "object",
"required": [
"type",
"data"
],
"properties": {
"type": {
"type": "string",
"enum": [
"background_color",
"banner_images",
"description_box"
]
},
"data": {
"type": "object"
}
},
"allOf": [
{
"if": {
"properties": {
"type": {
"const": "banner_images"
}
}
},
"then": {
"properties": {
"data": {
"required": [
"images"
],
"properties": {
"images": {
"type": "array"
}
}
}
}
}
}
]
}
}
For reference, here's the part of the JSON Schema draft-7 spec document that details the behaviour: https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-6.6

How to combine a property type, to match another properties type

I have the following use case with a JSON schema. I have a metadata object of a setting. In our case a setting can be of type string/real/integer/boolean.
In this object I have 4 fields: default/minimum/maximum each define a property of the setting.
Now what I want to achieve is that when the type of de default value is an integer, also the minimum/maximum values are integers.
The schema I have come up with so far:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"setting-value": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
}
]
},
"setting-meta": {
"type": "object",
"required": [
"name",
"type",
"default"
],
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"Real",
"Integer",
"Boolean",
"String"
]
},
"minimum": {
"$ref": "#/definitions/setting-value"
},
"maximum": {
"$ref": "#/definitions/setting-value"
},
"default": {
"$ref": "#/definitions/setting-value"
},
"value": {
"$ref": "#/definitions/setting-value"
}
}
}
}
}
Here it is possible for the #/definitions/setting-meta to have support for the different types. However it does not define that if for example the value of TYPE is equal to Real/Integer that the types of minimum/maximum/default/value should all be of type number.
I would use these definitions as follows
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "schema-definition-above.json#/definitions/setting-meta"
}
According the the current schema, all examples below are VALID, however they should be valid/invalid as suggested:
Valid JSon object:
{
"name": "Enabled",
"type": "Boolean",
"minimum": false,
"maximum": true,
"default": true,
"value": true
}
Invalid json object, minimum/maximum/default don't have the same type:
{
"name": "Enabled",
"type": "Boolean",
"minimum": false,
"maximum": 1,
"default": "value",
"value": true
}
Invalid json object: type, does not match the actual type of the values
{
"name": "Enabled",
"unit": "enabled/disabled",
"configId": "Accumulator",
"displayName": "Enable or disable this machine",
"type": "Integer",
"minimum": false,
"maximum": true,
"default": true,
"value": true
}
My question:
Is it possible to put these kinds of dependencies into a JSON schema? The only kind of dependency I have foudn so far is with property dependencies indicating that if one property is set, another should also be set.
Any help would be much appreciated.
EDIT:
Extended that use case with some JSON objects that should be validated or invalidated with the referenced schema.
In order to do conditional validation where you have a known set of possible conditions, you should use the if/then/else keywords, in combination with with allOf.
In this schema, the first schema in allOf defines your general structure and overall requirements. The second schema applies the then constraint if the if schema validates successfully.
You would need to replicate the second schema for each condition that you have.
You can see this schema working at https://jsonschema.dev (link is preloaded with the below schema and sample data)
(The use of patternProperties is just a space saver. You could define each property individually.)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"allOf": [
{
"properties": {
"type": {
"enum": [
"Real",
"Integer",
"Boolean",
"String"
]
}
},
"required": [
"type",
"default",
"name"
]
},
{
"if": {
"properties": {
"type": {
"const": "String"
}
}
},
"then": {
"patternProperties": {
"^(minimum|maximum|default|value)$": {
"type": [
"string"
]
}
}
}
}
]
}

JSON validation against JSON schemas: Why is this obvious JSON data not failing validation

This JSON file should fail validation but it does not. Someone tell me why.
Plug the below json data and schema into this web site, validate,
http://json-schema-validator.herokuapp.com
and I get the same results in the Mule Validate JSON Schema. Its obviously does not comply with the schema (i added some fields, I misspelled some fields, the date-time value is not a real date time) but yet it does not fail it. Can someone tell me why?
JSON Schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "http://hud.gov/ocio/xsd/esb/serviceauditingframework/2.0#",
"definitions": {
"serviceAuditLogData": {
"type": "object",
"title": "serviceAuditLogData",
"required": [
"serviceRequestTimestamp",
"sourceSystem"
],
"properties": {
"auditId": {
"type": "string"
},
"serviceRequestTimestamp": {
"type": "string",
"format": "date-time"
},
"serviceProvider": {
"type": "string"
},
"serviceProviderVersion": {
"type": "string"
},
"serviceProviderTimestamp": {
"type": "string",
"format": "date-time"
},
"eventType": {
"type": "string"
},
"eventDetail": {
"type": "string"
},
"hostName": {
"type": "string"
},
"sourceSystem": {
"type": "string"
},
"authenticationId": {
"type": "string"
},
"endUserId": {
"type": "string"
},
"inputData": {
"type": "string"
}
},
"propertiesOrder": [
"auditId",
"serviceRequestTimestamp",
"serviceProvider",
"serviceProviderVersion",
"serviceProviderTimestamp",
"eventType",
"eventDetail",
"hostName",
"sourceSystem",
"authenticationId",
"endUserId",
"inputData"
]
}
}
}
JSON Data
{
"serviceAuditLogData": {
"junk":"asdfasdf",
"serviceRequestTimestamp": "2004-09-29T12:58:31.470Z",
"serviceProvider": "FLQS",
"serviceProviderVersion": "v1.0.1",
"audit_id": "17f24136-2494-4bf8-9d3b-9baafaae0cc9",
"serviceProviderTimestamp": "2012-11-04T21:44:57.997Z",
"eventType": "Query Pool",
"eventDetail": "default pool",
"hostName": "esb-d-srv1.",
"sourceSystem": "LRS",
"authenticationId": "EsbLrsAccount",
"endUserId": "H574857",
"inputData": "L234234234, L32453462345, L23452346"
}
}
It does not fail because your schema does not enforce any constraint. Notice that definitions is not a jsonschema keyword that constraints validation. It is normally used to place sub-schemas that are re-used in other parts of the schema definition. Thus, to start with, you should change the definitions keyword for properties.
Another common misunderstanding with jsonschema is related to the properties keyword. Let's take the following example:
{
"type" : "object",
"properties" : {
"key1" : {
"type" : "string"
}
}
}
You must read it as: json must be an object, and in the case that it contains a key equal to key1, its value must be a string. According to that the following two json objects are valid:
{
"key2":12
}
And:
{
"key1":"sdf"
}
Finally, related to date-time format, you must check the section 6 of RFC3339 to be sure you have a valid date-time. And in any case, the implementation of formats is not compulsory in jsonschema validators.
Thanks #jruizaranguren I also learned that I needed to place
"additionalProperties": false, and "required": to make sure that whats' being passed in the API is what's expected.
The below is how I solved my problem.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"definitions": {
"serviceAuditLogData": {
"type": "object",
"additionalProperties": false,
"required": [
"auditCorrelationId",
"serviceRequestTimestamp",
"serviceProvider",
"serviceProviderVersion",
"serviceProviderTimestamp",
"eventType",
"hostName",
"sourceSystem",
"authenticationId"
],
"properties": {
"auditCorrelationId": {
"type": "string"
},
"serviceRequestTimestamp": {
"type": "string",
"format": "date-time"
},
"serviceProvider": {
"type": "string"
},
"serviceProviderVersion": {
"type": "string"
},
"serviceProviderTimestamp": {
"type": "string",
"format": "date-time"
},
"eventType": {
"type": "string"
},
"eventDetail": {
"type": "string"
},
"hostName": {
"type": "string"
},
"sourceSystem": {
"type": "string"
},
"authenticationId": {
"type": "string"
},
"endUserId": {
"type": "string"
},
"inputData": {
"type": "string"
}
}
}
},
"additionalProperties": false,
"required": [
"serviceAuditLogData"
],
"properties": {
"serviceAuditLogData": {
"$ref": "#/definitions/serviceAuditLogData"
}
}
}

A common JSON Schema for similar structure

I'm completely new to json and json schema, so I have a question (yet I don't know how much it make sense). Can we create a json schema which is common for similar type of structure. For example:
One single schema can be used to validate following json
JSON:
{
"Team_Table":
[{"Name":"New Zealand", "Match":"Six", "Won":"Six"}]
}
And
{
"Story_Taller":
[{"Story":"No Name", "Chapter":"Don't know"}]
}
Similarities:
Both have only one object in the array
Objects have string value.
Dissimilarities:
Number of properties are different
Keys are different in both
Can we do this?
Maybe this helps you along:
{
"properties": {
"Story_Taller": {
"type": "array",
"maxItems": 1,
"items": {
"properties": {
"Chapter": {
"type": "string"
},
"Story": {
"type": "string"
}
},
"additionalProperties": false
}
},
"Team_Table": {
"type": "array",
"maxItems": 1,
"items": {
"properties": {
"Name": {
"type": "string"
},
"Match": {
"type": "string"
},
"Won": {
"type": "string"
}
},
"additionalProperties": false
}
}
},
"oneOf": [
{
"title": "Story_Taller",
"required": [
"Story_Taller"
]
},
{
"title": "Team_Table",
"required": [
"Team_Table"
]
}
]
}
in (short) words:
in your JSON there must be one property of either "Story_Taller" or "Team_Table" with a maximum of 1 item
"oneOf": [ ... ]
Properties of both arrays are defined by items
"Story_Taller" must have "Chapter" and "Story" and no additional properties.
"Team_Table" must have "Name", "Match", "Won" and no additional properties.
And all of them are defined as strings.