Why jsonschema is not match with the json? - json

Given that, I have the below json:
{
"msg":"aaaa",
"email":"aaa#gmail.com"
}
Then, I wrote the below json schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Json schema sample",
"type": "object",
"additionalProperties": true,
"required": [
"msg",
"email"
],
"properties": {
"msg": {
"type": "string"
},
"email": {
"type": "string",
"pattern": "^[a-z0-9._%+-]+#[a-z0-9.-]+\.[a-z]{2,}$"
}
}
}
And sadly it is not matches with the json.
If you remove the ,"pattern": "^[a-z0-9._%+-]+#[a-z0-9.-]+\.[a-z]{2,}$" which is the regular expression for checking the email, then it works well. Please test it in this website.
I am sure that my regular expression for email is fine. But I don't know why it is not working in the json schema!

The \ is also an escape character in the json string, so now it's invalid json.
You could also see that when validating the json, for example on jsonlint.com
So your pattern needs a second \:
pattern": "^[a-z0-9._%+-]+#[a-z0-9.-]+\\.[a-z]{2,}$"
Full example: (don't forget to select draft-04 on https://jsonschemalint.com/, or change the schema to draft-07)
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Json schema sample",
"type": "object",
"additionalProperties": true,
"required": [
"msg",
"email"
],
"properties": {
"msg": {
"type": "string"
},
"email": {
"type": "string",
"pattern": "^[a-z0-9._%+-]+#[a-z0-9.-]+\\.[a-z]{2,}$"
}
}
}

Related

Unique value for a property validation using json schema

I have a JSON object like:
{
"result": [
{
"name" : "abc",
"email": "abc.test#mail.com"
},
{
"name": "def",
"email": "def.test#mail.com"
},
{
"name": "xyz",
"email": "abc.test#mail.com"
}
]
}
and schema for this:
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/object1607582431.json",
"title": "Root",
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"$id": "#root/result",
"title": "Result",
"type": "array",
"default": [],
"uniqueItems": true,
"items": {
"$id": "#root/result/items",
"title": "Items",
"type": "object",
"required": [
"name",
"email"
],
"properties": {
"name": {
"$id": "#root/result/items/name",
"title": "Name",
"type": "string"
},
"email": {
"$id": "#root/result/items/email",
"title": "Email",
"type": "string"
}
}
}
}
}
}
I am looking for an option to check uniqueness for email irrespective of name. How I can validate that every email should be unique?
You can't. There are no keywords that let you compare one particular data value against another, other than uniqueItems, which compares an array element in toto against another.
The JsonSchema specification does not currently support this.
You can see the active GitHub issue here: https://github.com/json-schema-org/json-schema-vocabularies/issues/22
However, there are various extensions of JsonSchema that do validate unique fields within lists of objects.
If you happen to be using Python you can use the package (I created) JsonVL. It can be installed with pip install jsonvl and then run with jsonvl data.json schema.json.
Code examples in the GitHub repo: https://github.com/gregorybchris/jsonvl

Conditionally Merging JSON Schema Properties

I'm trying to create a JSON schema to validate YAML for some VSCode intellisense. What I'm trying to do is choose the correct subschema to use for a property in the main schema based on an adjacent key's value.
Some JSON examples:
[
{
"name": "doesntmatter",
"matchMe": "stringToMatch:123whatever",
"mergeMe": {
"key1": "value1",
"key2": "value2"
}
}
]
[
{
"name": "doesntmatter",
"matchMe": "anotherStringToMatch:123whatever",
"mergeMe": {
"anotherKey": "valueSomething",
"anotherKey2": "cheese"
}
}
]
So I need to choose the correct schemas for the mergeMe objects based on the substring match of matchMe. After following a bunch of answers, I'm at a point where I can either make it match multiple, and error my linter, or match none, but an online validator says it's ok (except nothing matches as the required fields aren't triggering).
I moved my sub-schemas to be merged into definitions to reference them, and then used an if/then to match. That worked with one, but then I tried to expand it to do the tree matching, and I can't get that to work. Someone said that I should wrap my if/thens in an allOf (I'm not sure why that would work since surely not all of them would match?). Changing it to an anyOf makes none of them match and I get no intellisense. Nor do I really understand why I should wrap single if/thens or thens in allOfs.
The idea is that based on the pattern it uses a definitions schema to match the mergeMe property, but the conditional logic isn't quite right. Thinned schema below:
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "http://example.com/example.json",
"type": "array",
"title": "The root schema",
"description": "The root schema comprises the entire JSON document.",
"default": [],
"additionalItems": true,
"definitions": {
"stringToMatch": {
"$id": "#/definitions/stringToMatch",
"type": "object",
"properties": {
"key1": {
"type": "string"
}
},
"required": [
"key1"
],
"additionalProperties": true
},
"anotherStringToMatch": {
"$id": "#/definitions/anotherStringToMatch",
"type": "object",
"properties": {
"key2": {
"type": "string"
}
},
"required": [
"key2"
],
"additionalProperties": true
}
},
"items": {
"$id": "#/items",
"type": "object",
"title": "main schema",
"description": "An explanation about the purpose of this instance.",
"default": {},
"examples": [],
"required": [
"name",
"matchMe",
"mergeMe"
],
"properties": {
"name": {
"$id": "#/items/name",
"type": "string",
"title": "The name schema",
"description": "An explanation about the purpose of this instance.",
"default": "",
"examples": []
},
"matchMe": {
"$id": "#/items/matchMe",
"type": "string",
"title": "The matchMe schema",
"description": "An explanation about the purpose of this instance.",
"default": "",
"examples": []
}
},
"allOf": [
{
"if": {
"properties": {
"matchMe": {
"pattern": "^stringToMatch:[0-9.]+"
}
}
},
"then": {
"allOf": [
{
"type": "object",
"properties": {
"mergeMe": {
"$ref": "#/definitions/stringToMatch"
}
}
}
]
}
},
{
"if": {
"properties": {
"gear": {
"pattern": "^anotherStringToMatch:[0-9.]+"
}
}
},
"then": {
"allOf": [
{
"type": "object",
"properties": {
"mergeMe": {
"$ref": "#/definitions/anotherStringToMatch"
}
}
}
]
}
}
],
"additionalProperties": true
}
}
What I want in JS would look something like
const schema = { name, matchMe }
if (matchMe == "string1") schema.mergeMe = ...subschema1;
else if (...)
else if (...)
but I just can't really work it out. Can someone help?
Edit: jsonschema.dev playground - the idea being if I specify the food as prefixed by "fruit" I have to give it "pips" and "berry", whereas if I specify "vegetable" I have to give it a totally differet schema, and they don't overlap.
https://jsonschema.dev/s/pHzGo
This actually ended up being a bug in the VSCode YAML extension that was ingesting my schema, causing the if blocks to not evaluate, and has been raised, fixed and released.

Can a property of an allOf item contain a $ref to another allOf items definition?

Is "#/allOf/1/properties/body/properties/foo" a valid $ref?
As stated in this article which explains $ref usage, it looks like it should be.
However both AJV and https://www.jsonschemavalidator.net/ seem to disagree.
Trying with a plain JSON Pointer it definitely seems to be possible: https://repl.it/repls/WretchedSpiritedMammoth
This is the schema I'm testing with.
{
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"allOf": [
{
"$schema": "http://json-schema.org/draft-06/schema#",
"$id": "http:/example.org/example.schema.json",
"type": "object",
"properties": {
"type": {
"type": "string"
},
"body": {
"type": "object"
}
},
"required": [
"type",
"body"
]
},
{
"properties": {
"body": {
"$schema": "http://json-schema.org/draft-06/schema#",
"$id": "http://example.org/foo.schema.json",
"type": "object",
"properties": {
"foo": {
"type": "number",
"minimum": 1
},
"bar": {
"$ref": "#/allOf/1/properties/body/properties/foo"
}
},
"required": [
"foo",
"bar"
]
}
}
}
]
}
EDIT: These are the errors im getting:
jsonschemavalidator.net
Error parsing schema
Message: Error when resolving schema reference '#/allOf/1/properties/body/properties/foo'.
Path 'allOf[1].properties.body.properties.bar', line .., position ..
AJV:
can't resolve reference #/allOf/1/properties/body/properties/foo from id http://example.org/foo.schema.json"
Your schema is mostly right, although you've set an $id in a subschema.
The "$id" keyword defines a URI for the schema, and the base URI that
other URI references within the schema are resolved against.
https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-00#section-9.2
By adding an $id to a subschema, you've reset the base URI for other URI references, which includes any use of $ref within the subschema and its children.
$ref
...Resolved against the current URI base, it identifies the URI of a
schema to use.
https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-00#section-8
By changing your definition of bar to the following, your schema will be valid.
"bar": {
"$ref": "#/properties/foo"
}
Further edit as Relequestual requested in his later comment:
It is valid to have an absolute URI as subschema $id, but as noted it resets the base URI. The restrictions on $id values only apply when using it to create local URI fragment identifiers (if this does not mean anything to you, do not worry about it).
Referencing a property schema from another property schema directly is valid, but the more common best practice is to put such a schema under the "definitions" keyword and have both properties refer to that location. This is what I would recommend for maximum clarity:
{
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"allOf": [
{
"$schema": "http://json-schema.org/draft-06/schema#",
"$id": "http:/example.org/example.schema.json",
"type": "object",
"properties": {
"type": {
"type": "string"
},
"body": {
"type": "object"
}
},
"required": [
"type",
"body"
]
},
{
"properties": {
"body": {
"$schema": "http://json-schema.org/draft-06/schema#",
"$id": "http://example.org/foo.schema.json",
"type": "object",
"properties": {
"foo": {
"$ref": "#/definitions/whateverYouWantToCallIt"
},
"bar": {
"$ref": "#/definitions/whateverYouWantToCallIt"
}
},
"required": [
"foo",
"bar"
]
}
},
"definitions": {
"whateverYouWantToCallIt": {
"type": "number",
"minimum": 1
}
}
}
]
}

JSON schema validation with Map of string with Enum constraints

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]"
}
}
}

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"
}
}
}