JSON Schema - Allow only specific enum values for a property and reject the rest - json

Say I have the following JSON that I'd like validated.
[
{
"UpStatus":"Closed"
},
{
"UpStatus":"Open"
}
]
I want the json to pass validation only if there is at least one 'UpStatus' in the array defined to either 'Open' or 'Locked'.
If 'UpStatus' is not found as set to 'Open' or 'Locked' in the array, and is set to something else that is arbitrary say "Closed", I want the validation to fail.
I tinkered around with anyOf and came up with the following schema.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": [
{
"type": "object",
"properties": {
"UpStatus": {
"type": "string"
}
},
"minItems": 1,
"anyOf": [
{
"properties": {
"UpStatus": {
"const": "Open"
}
},
"required": [
"UpStatus"
]
},
{
"properties": {
"UpStatus": {
"const": "Locked"
}
},
"required": [
"UpStatus"
]
}
]
}
]
}
The above does not work correctly as it allows the following to pass which I thought it should fail to validate.
[
{
"UpStatus": "Closed"
},
{
"UpStatus": "Closed"
}
]
I played with the json schema for a long time and looked at examples and read some docs but could not get it to work. Any help is appreciated. Thank you.

In your schema above, you put the "minItems" keyword inside "items", which does nothing -- it needs to be adjacent to "items". But using "items" also means that all items must match, not just one.
Instead, use "contains":
{
"type: "array",
"contains": {
"type": "object",
"required": ["UpStatus"],
"properties": {
"UpStatus": {
"enum": ["Open","Locked"],
}
}
}
}
Translation: the data must be an array, where at least one element must be an object, which has the property "UpStatus" with value either "Open" or "Locked".
You may want all items in the array to conform to something specific, in which case you use "items" to specify that. The difference between "items" and "contains" is that the "items" schema must match all items, whereas the "contains" schema only has to match one.
HOWEVER, "contains" is not available in the draft 4 version of the spec. Is there any chance you can upgrade? There is a list of implementations in various languages here. Alternatively, you can simulate the "contains" keyword with "not": { "items": { "not": { ... schema ... } } } (courtesy Jason Desrosiers).
addendum: When I evaluate your schema and data, it does not pass, but rather produces these errors, so perhaps your implementation is buggy (or you mispasted something):
{
"errors" : [
{
"error" : "value does not match",
"instanceLocation" : "/0/UpStatus",
"keywordLocation" : "/items/0/anyOf/0/properties/UpStatus/const"
},
{
"error" : "not all properties are valid",
"instanceLocation" : "/0",
"keywordLocation" : "/items/0/anyOf/0/properties"
},
{
"error" : "value does not match",
"instanceLocation" : "/0/UpStatus",
"keywordLocation" : "/items/0/anyOf/1/properties/UpStatus/const"
},
{
"error" : "not all properties are valid",
"instanceLocation" : "/0",
"keywordLocation" : "/items/0/anyOf/1/properties"
},
{
"error" : "no subschemas are valid",
"instanceLocation" : "/0",
"keywordLocation" : "/items/0/anyOf"
},
{
"error" : "not all items are valid",
"instanceLocation" : "",
"keywordLocation" : "/items"
}
],
"valid" : false
}

Related

How can I specify in a json schema that a certain property is mandatory and also must contain a specific value?

I want to create several json schemas for different scenarios.
For scenario 1 I would like to specify that:
a) The property "draftenabled" must have the value true.
b) the property "draftenabled" does exist.
I have checked this post
Validating Mandatory String values in JSON Schema
and tried the following
I tried to validate this json
{
"$schema": "./test-schema.json",
"draftenabled": false,
"prefix": "hugo"
}
with this schema test-schema.json that I had created in Visual Studio Code.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"$schema": {
"type": "string"
},
"draftenabled": {
"type": "boolean"
},
"prefix": {
"type": "string"
}
},
"additionalItems": false,
"contains": {
"properties": {
"draftenabled": {
"const": true
}
},
"required": [
"draftenabled"
]
}
}
I would have expected an error since the value for draftenabled is false rather than true.
It looks like there is some confusion around how the keywords apply to instances (data) of different types.
properties only applies to objects
additionalItems and contains only apply to arrays
Since your instance is an object, additionalItems and contains will be ignored.
Based on your description of what you want, I would do something like the following:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"$schema": {
"type": "string"
},
"draftenabled": {
"const": "true"
},
"prefix": {
"type": "string"
}
},
"required": [
"draftenabled"
]
}
This moves the definitions you have in the contains into the main schema. You got that bit right, just in the wrong place.
You also mention that this is a "scenario 1." If there are other scenarios, I suggest creating schemas like this for each scenario then wrapping all of them together in a oneOf or anyOf:
{
"oneOf": [
{ <scenario 1> },
{ <scenario 2> },
...
]
}

In Logic Apps JSON Array while parsing throwing error for single object but for multiple objects it is working fine

While parsing JSON in Azure Logic App in my array I can get single or multiple values/objects (Box as shown in below example)
Both type of inputs are correct but when only single object is coming then it is throwing an error "Invalid type. Expected Array but got Object "
Input 1 (Throwing error) : -
{
"MyBoxCollection":
{
"Box":{
"BoxName": "Box 1"
}
}
}
Input 2 (Working Fine) : -
{
"MyBoxCollection":
[
{
"Box":{
"BoxName": "Box 1"
},
"Box":{
"BoxName": "Box 2"
}
}]
}
JSON Schema :
"MyBoxCollection": {
"type": "object",
"properties": {
"box": {
"type": "array",
items": {
"type": "object",
"properties": {
"BoxName": {
"type": "string"
},
......
.....
..
}
Error Details :-
[
{
"message": "Invalid type. Expected Array but got Object .",
"lineNumber": 0,
"linePosition": 0,
"path": "Order.MyBoxCollection.Box",
"schemaId": "#/properties/Root/properties/MyBoxCollection/properties/Box",
"errorType": "type",
"childErrors": []
}
]
I used to use the trick of injecting a couple of dummy rows in the resultset as suggested by the other posts, but I recently found a better way. Kudos to Thomas Prokov for providing the inspiration in his NETWORG blog post.
The JSON parse schema accepts multiple choices as type, so simply replace
"type": "array"
with
"type": ["array","object"]
and your parse step will happily parse either an array or a single value (or no value at all).
You may then need to identify which scenario you're in: 0, 1 or multiple records in the resultset? I'm pasting below how you can create a variable (ResultsetSize) which takes one of 3 values (rs_0, rs_1 or rs_n) for your switch:
"Initialize_ResultsetSize": {
"inputs": {
"variables": [
{
"name": "ResultsetSize",
"type": "string",
"value": "rs_n"
}
]
},
"runAfter": {
"<replace_with_name_of_previous_action>": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Check_if_resultset_is_0_or_1_records": {
"actions": {
"Set_ResultsetSize_to_0": {
"inputs": {
"name": "ResultsetSize",
"value": "rs_0"
},
"runAfter": {},
"type": "SetVariable"
}
},
"else": {
"actions": {
"Set_ResultsetSize_to_1": {
"inputs": {
"name": "ResultsetSize",
"value": "rs_1"
},
"runAfter": {},
"type": "SetVariable"
}
}
},
"expression": {
"and": [
{
"equals": [
"#string(body('<replace_with_name_of_Parse_JSON_action>')?['<replace_with_name_of_root_element>']?['<replace_with_name_of_list_container_element>']?['<replace_with_name_of_item_element>']?['<replace_with_non_null_element_or_attribute>'])",
""
]
}
]
},
"runAfter": {
"Initialize_ResultsetSize": [
"Succeeded"
]
},
"type": "If"
},
"Process_resultset_depending_on_ResultsetSize": {
"cases": {
"Case_no_record": {
"actions": {
},
"case": "rs_0"
},
"Case_one_record_only": {
"actions": {
},
"case": "rs_1"
}
},
"default": {
"actions": {
}
},
"expression": "#variables('ResultsetSize')",
"runAfter": {
"Check_if_resultset_is_0_or_1_records": [
"Succeeded",
"Failed",
"Skipped",
"TimedOut"
]
},
"type": "Switch"
}
For this problem, I met another stack overflow post which is similar to this problem. While there is one "Box", it will be shown as {key/value pair} but not [array] when we convert it to json format. I think it is caused by design, so maybe we can just add a record "Box" at the source of your xml data such as:
<Box>specific_test</Box>
And do some operation to escape the "specific_test" in the next steps.
Another workaround for your reference:
If your json data has only one array, we can use it to do a judgment. We can judge the json data if it contains "[" character. If it contains "[", the return value is the index of the "[" character. If not contains, the return value is -1.
The expression shows as below:
indexOf('{"MyBoxCollection":{"Box":[aaa,bbb]}}', '[')
The screenshot above is the situation when it doesn't contain "[", it return -1.
Then we can add a "If" condition. If >0, do "Parse JSON" with one of the schema. If =-1, do "Parse JSON" with the other schema.
Hope it would be helpful to your problem~
We faced a similar issue. The only solution we find is by manipulating the XML before conversion. We updated XML nodes which needs to be an array even when we have single element using this. We used a Azure function to update the required XML attributes and then returned the XML for conversion in Logic Apps. Hope this helps someone.

JSON schema conditional check on certain object attribute within an array

What I want to do either via AJV - JSON Schema validation or Custom Keywords (preferably I would go with this): The Array can have 1 or 2 JSON objects with type as 'admin' and 'guest'. The "type":"guest" object will always be there and "type":"admin" object is optional.
Extra Notes:
-The object itself may contain addition attributes and nested objects, in future
-The other valid enums aresuperadmin, admin,user and guest
-The type sequence in array is: superadmin, admin,user and guest. Is it possible to check the sequence? (although its optional)
-The 'guest' type object will always be there and there will be a unique type of object. If any re-occurence any object type (e.g. superadmin, admin,user and guest) then its an error
//Here is the schema:
{
"type": "object",
"properties": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": { "enum": ["guest", "admin"]
},
"rights": {"type": "string"},
"hyperLink": {"type": "string", "format": "uri"}
}
}
}
}
I need to add 'checkTypeAndValue' flag somewhere in the json so that I can grab the complete JSON object and the corresponding attributes to do a programmatic check?
const checkTypeAndValue = function (schema, completeJSONObj) {
//
};
ajv.addKeyword('checkTypeAndValue', {
validate: checkTypeAndValue,
errors: true
});
and here are some valid and invalid examples:
/Valid 1: As type is 'admin' and so 'rights' SHOULD NOT be in 'guest' object
{
[
{
"type":"admin",
"hyperLink": "http://www.someguest.com",
"rights":"all"
},
{
"type":"guest",
"hyperLink": "http://www.someadmin.com"
}
]
}
//Valid 2: You can have a SINGLE 'guest' object. 'admin' object is not required all the time
{
[
{
"type":"guest",
"hyperLink": "http://www.someadmin.com",
"rights": "limited" //MANDATORY or REQUIRED Attribute
}
]
}
//InValid
{
[
{
"type":"admin",
"hyperLink": "http://www.someguest.com",
"rights":"all"
},
{
"type":"guest",
"hyperLink": "http://www.someadmin.com",
"rights":"limited"
//Error ==> As rights=all is there in 1st object, you cannot set 'rights' to any value including blank even having 'rights' attribute is not valid.
}
]
}
Here is the if else condition that I need to sort out:
//Assuming admin object exist with rights....
if( type == admin && rights != ""){
if(type == guest && rights attribute is there && rights != ""){
//The 'guest' object will always be there....
//error: guest 'rights' cannot have a value if type is 'admin' and rights is 'all' or any other value.
}
}else{
//Assuming mandatory guest object exist with rights....
if(guest.rights does not exist OR guest.rights == "")
//Error: 'rights' is MANDATORY attribute in guest block and error if its empty
else
//Everything is fine
}
Also is there any way by which we can check in an array that there will be only one pair of object of certain type?
For example: only one 'guest, 'admin' type. Error, if there are more than one type of 'guest' or 'admin'
//Complete example
{
[
{
"type":"superadmin",
"hyperLink": "http://www.superadmin.com"
},
{
"type":"admin",
"hyperLink": "http://www.admin.com",
"rights":"all"
},
{
"type":"user",
"hyperLink": "http://www.user.com"
},
{
"type":"guest",
"hyperLink": "http://www.guest.com"
}
]
}
It may seem a little tricky to do this with JSON Schema, but it is possible.
You need to be able conditionally check a negative condition.
So, you'll need to use a combination of if and then, with not.
First the condition: if there's an admin user. Using if, we can then conditionally apply the further checking rule.
Next, the check: guest user cannot have rights.
then is applied when if passes validation. We then need to negate a check that a guest has rights, using not.
You can see this correctly failing validation with the example expected invalid JSON data you provided using https://jsonschema.dev (link is pre-loaded with this schema and your provided instance data. It uses AJV in browser btw.)
Update: I've updated the schema to meet your additional requirements.
I've also updated the demo link above.
The schema now only allows admin to have rights if admin is present, and no other user type may have rights if admin is present. Also one item in the array must have rights (as you required).
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"if": {
"contains": {
"required": [
"type"
],
"properties": {
"type": {
"const": "admin"
}
}
}
},
"then": {
"not": {
"contains": {
"required": [
"type",
"rights"
],
"properties": {
"type": {
"not": {
"const": "admin"
}
}
}
}
}
},
"contains": {
"type": "object",
"required": ["rights"]
},
"items": {
"type": "object",
"properties": {
"type": {
"enum": [
"guest",
"admin"
]
},
"rights": {
"type": "string"
},
"hyperLink": {
"type": "string",
"format": "uri"
}
}
}
}

jsonSchema validating numerical keys by pattern

Could you help me, how I can validate "keys"(ex. "1","2","3") of list items in following json:
{
"list" : {
"1" : {
"element1" : "1",
"element2" : "2"
},
"2" : {
"element1" : "1",
"element2" : "2"
},
....
"512" : {
"element1" : "1",
"element2" : "2"
}
}
}
Please give me common examples to validate keys in json, too.
JSON Schema has three ways to constraint the property names of an object.
properties
The properties keyword allows you to set an exact match for property names.
{
"type": "object",
"properties": {
"1": { ... },
"2": { ... },
...
}
}
patternProperties
The patternProperties keyword constrains any property name that matches a regular expression to validate against a given schema.
{
"type": "object",
"patternProperties": {
"[1-9][0-9]*": { ... }
}
}
propertyNames
The propertyNames keyword constraints property names to match the given schema.
{
"type": "object",
"propertyNames": {
"pattern": "[1-9][0-9]*"
}
}
Edit 12-19-2018 Improve answer, add missing patternProperties, add new (since original answer) option propertyNames.

How to make a "patternProperty" required in JSON Schema (Ruby)

Consider the following JSON :
{
"1234abcd" : {
"model" : "civic"
"made" : "toyota"
"year" : "2014"
}
}
consider another JSON :
{
"efgh56789" : {
"model" : "civic"
"made" : "toyota"
"year" : "2014"
}
}
the outermost alphanumeric key will vary and required, if the key was fixed; let's say "identifier" then the schema was straightforward, however since the key-name is variable, we have to use patternProperties, how can I come up with a schema that captures these requirement for the outermost key:
property name (key) is variable
required
alphanumeric lowercase
using json-schema : https://github.com/ruby-json-schema/json-schema in ruby.
The best you can do to require properties when those properties are variable is to use minProperties and maxProperties. If you want to say there must be one and only one of these alphanumeric keys in your object, you can use the following schema. If you want to say there has to be at least one, you could just leave out maxProperties.
{
"type": "object",
"patternProperties": {
"^[a-z0-9]+$": {
"type": "object",
"properties": {
"model": { "type": "string" },
"make": { "type": "string" },
"year": { "type": "string" }
},
"required": ["model", "make", "year"]
}
},
"additionalProperties": false,
"maxProperties": 1,
"minProperties": 1
}
You may have to change the regular expression to fit your valid keys:
{
"patternProperties": {
"^[a-zA-Z0-9]*$":{
"properties": {
"model":{"type":"string"},
"made":{"type":"string"},
"year":{"type":"string"}
}
}
},
"additionalProperties":false
}