I am trying to define a json schema to limit the properties of objects conatined in an array.
What I have so far is:
{
"title":"myCollection",
"properties":{
"things":{
"type":"array",
"items":[{
"title":"thingObj",
"type":"object",
"properties":{
"name":{
"type":"string"
},
"code":{
"type":"string"
},
"type":{
"type":"string",
"enum":["dog","cat"]
},
"rate":{
"type":"number"
},
"value":{
"type":"number"
}
},
"anyOf":[{
"properties":{
"name":{
"type":"string"
}
},"required":["name"]
},{
"properties":{
"code":{
"type":"string"
}
},"required":["code"]
},{
"properties":{
"type":{
"type":"string",
"enum":["new","existing"]
}
},"required":["type"]
}],
"oneOf":[{
"properties":{
"rate":{
"type":"number"
}
},
"required":["rate"]
},{
"properties":{
"value":{
"type":"number"
}
},
"required":["value"]
}],
"additionalProperties":false
}]
}
}
}
Now given the following jsonobj:
{
"things": [
{
"name": "tabby",
"code": "meeow",
"type": "cat",
"value": 20
},
{
"name": "k9",
"code": "woofer",
"type": "dog",
"rate": 15
}
]
}
This json schema validator delivers a valid response but this validation only seems to apply to the first element in the array. If you remove all the fields included in the anyOf clause or the oneOf clause on the first element then validation fails. The same on the second array element does not raise the desired failure. How can I ensure the validation is run against each array member?
This is because you have (accidentally) used "tuple typing". This is enabled when the value of "items" is an array, and it matches schemas to specific positions in the array.
If you change "items" (in your schema) to be simply a schema (not an array of schemas), then it will validate all items the same way.
Kudos to #cloudfeet 's answer, I was struggling with this issue until I saw his answer.
To be more clear, the [] around items should be removed.
{
"title":"myCollection",
"properties":{
"things":{
"type":"array",
"items":**[**{
"title":"thingObj",
"type":"object",
.
.
.
"required":["value"]
}**]**,
"additionalProperties":false
}]
}
}
}
Related
Im trying to find a way to validate flattened json-keys. For example, lets say I have a schema defined as below:
{
"$schema":"http://json-schema.org/draft-04/schema#",
"title":"Employee",
"type":"object",
"additional_properties":false,
"properties":{
"emp_category":{
"type":"string",
"oneOf":[
{
"enum":[
"INDIVIDUAL_CONTRIBUTOR",
"MANAGER"
]
}
]
},
"id":{ "type":"string" },
"emp_meta":{
"oneOf":[
{ "$ref":"#/definitions/IndividualContributor" },
{ "$ref":"#/definitions/Manager" }
]
}
},
"required":[
"emp_category",
"id"
],
"definitions":{
"IndividualContributor":{
"title":"IndividualContributor",
"type":"object",
"properties":{
"name":{ "type":"string" },
"id":{ "type":"string" },
"department":{ "type":"string" },
"managed_by":{ "type":"string" }
},
"required":[
"id",
"department",
"managed_by"
]
},
"Manager":{
"title":"Manager",
"type":"object",
"properties":{
"name":{ "type":"string" },
"id":{ "type":"string" },
"department":{ "type":"string" },
"managed_by":{ "type":"string" },
"manages":{
"type":"array",
"items":{ "type":"string" }
}
},
"required":[
"id",
"department",
"managed_by"
]
}
}
}
Now, we want to expose some upstream REST API to be able to query over objects pertaining to above schemas, lets say we have a REST payload as below:
{
"empoloyee":{
"AND":[
{ "emp_category":"MANAGER" },
{ "emp_meta.department":"R&D" },
{ "emp_meta.manages":"John*" }
]
}
}
So, I am wondering if theres a generalized way to validate flattened json keys that are part of the query payload. I've tried to parse (dfs) the query payload by leaf and convert it into a dict object and validate against the schema. But required fields are making this quite challenging. So, wondering if theres a way to go about it. I'm open to considering a different design as well, especially as keys and objects can become deeply nested.
{
"$schema":"https://json-schema.org/draft/2019-09/schema",
"$id":"PersonalDetails.json",
"type":"object",
"properties":{
"Header":{
"type":"object",
"properties":{
"HeaderName":{
"type":"string"
},
"HeaderValue":{
"type":"string"
}
}
},
"Details":{
"type":"array",
"items":{
"type":"object",
"properties":{
"FName":{
"type":"string"
},
"LName":{
"type":"string"
},
"Address":{
"type":"object",
"properties":{
"FlatNo":{
"type":"string"
},
"Sector":{
"type":"string"
},
"LandMarks":{
"type":"object",
"properties":{
"LandMark1":{
"type":"string"
},
"LandMark2":{
"type":"string"
}
}
}
},
"required":[
"Sector"
]
}
}
}
}
}
}
{
"Header":{
"HeaderName":"DummyName",
"HeaderValue":"DummyName"
},
"Details":[
{
"FName":"Chicago",
"LName":"Laos",
"Address":{
"FlatNo":"Excalibur",
"Sector":"07",
"LandMarks":{
"LandMark1":"USA",
"LandMark2":"UK"
}
}
}
]
}
So the requirement is to insert IF then conditions in Json Schema with following conditions
if LandMark1 = USA and FlatNo = Excalibur
then Sector is required
I am not able to figure out where exactly should i place this condition and its syntax
i tried using $ref in if then conditions in allOf in Details as its parent for both Address and LandMarks
but even that didn't work as it was not able to figure out reference
Any help is highly apprecitiable
You'll need to use the if/then keywords to describe this. The hard part is the if schema.
You need to define an if schema that will validate to true when given an instance where your conditions are met. It can help to develop this schema separately and then add it to your full schema.
{
"type": "object",
"properties": {
"FlatNo": { "const": "Excalibur" },
"LandMarks": {
"type": "object",
"properties": {
"LandMark1": { "const": "USA" }
},
"required": ["LandMark1"]
}
},
"required": ["LandMarks", "FlatNo"]
}
The following instance would pass validation against that schema. It would fail if FlatNo != Excalibur or LandMark1 != USA.
{
"FlatNo":"Excalibur",
"Sector":"07",
"LandMarks":{
"LandMark1":"USA",
"LandMark2":"UK"
}
}
Now it should be trivial to apply if/then to your address schema.
"if": { ... schema from above ... },
"then": { "required": ["Sector"] }
I'd like to define an object something like this, to map to something like a java Map (but not exactly, since this needs to interface with typescript as well). Ultimately, what I'm trying to figure out is how to declare the <T> type of something (say Dict<MyObj>) in another object (if this is even possible):
(this is obviously not a valid json schema)
{
"type": "object",
"javaType": "some.package.base.DictKeyValuePair",
"properties": {
"key": { "type": "string" },
"value": { "type": "<T>" }
}
}
which would be referenced in other json schemas something like this:
{
"type": "object",
"javaType": "some.package.base.Dict",
"properties": {
"key": { "type": "string" },
"value": { "type": "<T>" },
"keValuePairs": {
"type": "array",
"items": {
"$ref": "DictKeyValuePair.json",
"genericType": "<T>"
}
}
}
}
finally being used in an object similar to this:
{
"type": "object",
"javaType": "some.package.SomeObject",
"properties": {
"someDict": {
"$ref": "Dict.json",
"genericType": "SomeObjectOrSimpleType"
}
}
}
So... is this possible in a json schema?
It is possible to do something like that using json-schema but you need to define the schemas, take a look to this samples:
http://json-schema.org/learn/examples/address.schema.json
{
"$id":"https://example.com/address.schema.json",
"$schema":"http://json-schema.org/draft-07/schema#",
"description":"An address similar to http://microformats.org/wiki/h-card",
"type":"object",
"properties":{
"post-office-box":{
"type":"string"
},
"extended-address":{
"type":"string"
},
"street-address":{
"type":"string"
},
"locality":{
"type":"string"
},
"region":{
"type":"string"
},
"postal-code":{
"type":"string"
},
"country-name":{
"type":"string"
}
},
"required":[
"locality",
"region",
"country-name"
],
"dependencies":{
"post-office-box":[
"street-address"
],
"extended-address":[
"street-address"
]
}
}
I got the below schema from http://json-schema.org/examples.html, i want to know if the required keyword can only come at the top level. or it can also come within the properties if there is a property of type object.I could not find any thing related to this in the specification https://datatracker.ietf.org/doc/html/draft-fge-json-schema-validation-00#section-5.4.3.
{
"title": "Example Schema",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
}
},
"required": ["firstName", "lastName"]
}
So the below example is a valid schema
{
"title":"Example Schema",
"type":"object",
"properties":{
"firstName":{
"type":"string"
},
"lastName":{
"type":"string"
},
"age":{
"type":"object",
"properties":{
"minAge":{
"type":"number"
},
"maxAge":{
"type":"number"
},
"required":[
"minAge",
"maxAge"
]
}
}
},
"required":[
"firstName",
"lastName"
]
}
4.4 Keywords with the possibility to validate container instances (arrays
or objects) only validate the instances themselves and not their
children (array items or object properties).
So I see that yes, you can have those on any level but the validation should be only consider on the same level as required
Yes, required is a valid keyword in any schema. There are no restrictions for nested schemas.
To use your example, the following is a valid schema and will validate the way you want it to.
{
"title": "Example Schema",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"type": "object",
"properties": {
"minAge": {
"type": "number"
},
"maxAge": {
"type": "number"
}
},
"required": [
"minAge",
"maxAge"
]
}
},
"required": [
"firstName",
"lastName"
]
}
The required keyword can be present in any schema. This is true of all schema keywords.
(There is a special-case for the meta-keyword $schema, for which it is advisable to only have in the top level)
Let's say I have the following JSON Schema
{
"name":"Product",
"type":"object",
"properties":{
"id":{
"type":"number",
"required":true
},
"name":{
"description":"Name of the product",
"required":true
},
"price":{
"required":true,
"type": "number",
"minimum":0,
"required":true
},
"tags":{
"type":"array",
"items":{
"type":"any"
}
}
}
}
But, instead of tags being an array, I'd like it to be part of the root schema. So you could specify any property, but I give special attention to "id", "name" and "price"
Which of the following would be the correct way of doing it, and which ones are completely wrong?
{
"name":"Product",
"type":"object",
"properties":{
"id":{
"type":"number",
"required":true
},
"name":{
"description":"Name of the product",
"required":true
},
"price":{
"required":true,
"type": "number",
"minimum":0,
"required":true
}
},
"additionalProperties": {
"type":"any"
}
}
{
"name":"Product",
"type":"object",
"properties":{
"id":{
"type":"number",
"required":true
},
"name":{
"description":"Name of the product",
"required":true
},
"price":{
"required":true,
"type": "number",
"minimum":0,
"required":true
}
},
"extends": {
"type":"any"
}
}
{
"name":"Product",
"type":["object","any"],
"properties":{
"id":{
"type":"number",
"required":true
},
"name":{
"description":"Name of the product",
"required":true
},
"price":{
"required":true,
"type": "number",
"minimum":0,
"required":true
}
}
}
I can come up with a few more (such as inverting roles of "any" and "object"), but they are all derivative of these three examples.
[disclaimer: author of the next JSON Schema validation spec here]
OK, it is unclear what you ask, see below, but one of your examples clearly does not do what you want, this is the one where you write:
{ "type": [ "object", "any" ] }
This is equivalent to an empty schema, and as such validates each and every instance.
One way I read your question is that you want your JSON data to be either a tag array or an object with at least members named id, name and price. Since you seem to be using draft v3, you only have one solution:
{
"type": [
{
"description": "schema for tags array here",
},
{
"description": "schema for the base object here"
}
]
}
This construct means that the instance must obey at least one schema inside type (and you can mix it with primitive types as well).
However: the current draft now being v4, you should now write:
{
"anyOf": [
{
"description": "schema for tags array here",
},
{
"description": "schema for the base object here"
}
]
}
Note that not all implementations support draft v4, or the construct for type above. In fact, very few do. See below for a link to an online schema validator which supports both.
Another way that I read your question is that you want to allow properties other than id, name and price to be whatever they like. This is then quite simple. Just don't define a schema for tags in properties:
{
"type": "object",
"required": [ "id", "name", "price" ]
"properties": {
"id": {
"type": "number"
},
"name": {
"description": "Name of the product"
},
"price": {
"type": "number",
"minimum": 0
}
}
}
Since you don't specify additionalProperties as being false, the object instance can have any number of additional members, and these members can be anything.
Link to an online validator which can test your schemas: here