JSON Hyper-Schema: different schemas for GET and POST - json

I want to describe an API that has fields which allows for different ways to define values when POSTing an item, but only ever output in the field in one specific way.
For example, I might want to describe an API where an item can be created or updated like this: {"name": "Task", "due": "2014-12-31"} or like this: {"name": "Task", "due": {"$date": 1419984000000}}, but it is only ever returned from the API in the first way.
The schema for POST/PUT could therefore be:
{
"type": "object"
"properties": {
"name": {
"type": "string"
},
"due": {
"oneOf": [
{
"type": "string",
"format": "date"
},
{
"type": "object",
"properties": {
"$date": {
"type": "number"
}
},
"required": ["$date"],
"additionalProperties": false
}
]
}
}
}
Whereas the schema for access via GET would be much simpler:
{
"type": "object"
"properties": {
"name": {
"type": "string"
},
"due": {
"type": "string",
"format": "date"
}
}
}
It would be good for consumers of the API to know that they only have to account for one possible output method rather then all of them.
Is there any accepted standard approach to specify the different schemas within the context of JSON Hyper-Schema? I've thought about specifying these differences via the "links" property, but I do not know what "rel" I would define these schemas under and it seems very-non-standard.

If I understood correctly, and you want to specify one schema per operation you can do it with standard hyper-schema. Let's see and example for a post operation:
{
"description": "create an item.",
"href": "/items",
"method": "POST",
"rel": "create",
"schema": {
"$ref": "#/api/createitem"
},
"title": "Create an item"
}
The actual schema that is required is referenced in "schema" property through "$ref".
If you also wanted to describe the response types, then you could use "targetSchema" property. Be aware that this is advisory only (as it is explained in the docs)

Related

How to make a required property if another property is empty and vice versa, in the JSON Schema

My current json schema definition is like this
{
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"description": {
"type": "string",
"minLength": 1
},
"input": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"type": {
"type": "string"
}
},
"required": ["name", "description", "type"]
}
},
"output": {
"type": "array",
"maxItems": 1,
"items": {
"type": "object",
"properties": {
"description": {
"type": "string",
"minLength": 1
},
"type": {
"type": "string"
}
},
"required": ["description", "type"]
}
}
},
"required": ["name", "description"]
}
So I need to validate the scheme for the following conditions:
If input array and output array are empty, both must be required;
If the input array is not empty, then the output array should not be required;
If the output array is not empty, then the input array should not be required;
Thank you in advance.
Your first condition is the only one that we need to deal with. All properties are optional by default, so your conditions 2 and 3 translate to something like, "if the input array is not empty, then do nothing".
There are a couple of ways to achieve the first condition, but I suggest the following.
"allOf": {
"if": {
"properties": {
"input": { "const": [] },
"output": { "const": [] }
}
},
"then": { "required": ["input", "output"] }
}
it seems like all three of your requirements are self-fulfilling in json schema.
If input array and output array are empty, both must be required
if input and output are empty arrays, they are already present, so saying they are required is redundant. sort of, "if x is present with the value [], then x must be present". Jason's schema correctly expresses the way you've phrased this, but I don't think there's any way for that schema to cause a validation error.
and Jason's answer is correct on points 2 and 3.
I'd suggest you think about some example instances you would expect to fail validation (and add them to your question here), and that will help to construct a schema that adds the proper constraints.

Using multiple anyOf inside oneOf

I wanted to create a schema where I will be having multiple objects inside "oneOf" which will be having many objects in anyOf format where some of the keys can be of required type(this part works)
My schema :-
{
"description": "schema v6",
"type": "object",
"oneOf": [
{
"properties": {
"Speed": {
"items": {
"anyOf": [
{
"$ref": "#/definitions/speed"
},
{
"$ref": "#/definitions/SituationType"
}
]
},
"required": [
"speed"
]
}
},
"additionalProperties": false
}
],
"definitions": {
"speed": {
"description": "Speed",
"type": "integer"
},
"SituationType": {
"type": "string",
"description": "Situation Type",
"enum": [
"Advice",
"Depend"
]
}
}
}
But when I'm trying to verify this schema but i'm able to authenticate some incorrect values like
{
"Speed": {
"speed": "ABC",//required
"SituationType1": "Advisory1" //optional but key needs to be correct
}
}
correct response which i was expecting was
{
"Speed": {
"speed": "1",
"SituationType": "Advise"
}
}
First, you need to set the schema type correctly, otherwise implmentations may assume you're using the latest JSON Schema version (currently draft-7).
So, in your schema root, you need the following:
"$schema": "http://json-schema.org/draft-06/schema#",
Second, items is only applicable if the target is an array.
Currently your schema only checks the following:
If the root object has a property of "Speed", it must have a key of
"speed". The root object must not have any other properties.
And nothing else.
Your use of definitions and how you reference them is probably not what you intended.
It looks like you want Speed to contain speed which must be an integer, and optionaly SituationType which must be a string, limited by enum, and nothing else.
Here's the schema I have based on that, which passes and fails correctly based on your given example data:
{
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"oneOf": [
{
"properties": {
"Speed": {
"properties":{
"speed": {
"$ref": "#/definitions/speed"
},
"SituationType": {
"$ref": "#/definitions/SituationType"
}
},
"required": [
"speed"
],
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
"speed": {
"description": "Speed",
"type": "integer"
},
"SituationType": {
"type": "string",
"description": "Situation Type",
"enum": [
"Advice",
"Depend"
]
}
}
}
You need to define the properties for Speed, because otherwise you can't prevent additional properties, as additionalProperties is only effected by adjacent an properties key. We are looking to created a new keyword in draft-8 to support this kind of behaviour, but it doesn't look like you need it in your example (Huge Github issue in relation).
Adding additionalProperties false to the Speed schema now prevents other keys in that object.
I SUSPECT that given your question title, there may be more schema at play here, and you've simplified it for this question. If you have a more detailed schema with more complex issues, I'd be happy to help also.

Json-schema doesn't validate json with $ref-references

I have this json:
{
"categories": [
{
"id": 1,
"name": "cat1",
"topics": [
{
"$ref": "#/topics/1"
}
]
}
],
"topics": [
{
"id": 1,
"name": "top1"
}
]
}
And I've written the next schema to validate it:
{
"definitions": {
"category": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"topics": {
"type": "array"
"items": { "$ref": "#/definitions/topic" }
}
}
},
"topic": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
}
},
"type": "object",
"properties": {
"categories": {
"items": { "$ref": "#/definitions/category" },
"type": "array"
},
"topics": {
"items": { "$ref": "#/definitions/topic" },
"type": "array"
}
}
}
When I use this schema on popular online validators, it doesn't catch invalid references like #/topics/5 or #/ttt/555.
Can I use this schema to validate references? Can you suggest me library or service, that can do it?
Currently this is outside the scope of JSON Schema. The proposal #erosb mentions is still under consideration, but not for the soon-to-be-forthcoming draft-07. With enough demand it may be considered for draft-08. It would be a significant expansion of the project's scope, which is why it has been on hold while other things are addressed.
Some validators make it easy to define your own extension keywords, which may be a good way to do what you want. There are definitely libraries that will apply a JSON Pointer and let you find out if it points to anything or not. If you implement #erosb's proposal somewhere, it would be great if you could comment on the issue and let us know how it works out.
I'm not sure if I properly understand what you try to achieve. I assume you want to denote that the items of the "topics" array should be JSON references ("$ref" with a JSON Pointer) _and the pointed object should match the schema "#/definitions/topic".
If this is the case, then currently there is no way to express it with json schema, so - even with the latest version - you can only denote that a string should be a json pointer, but you can't make restrictions on what the type of the referenced object should be.
Last year I made a suggestion addressing this problem, but due to mixed feedback it got somewhat stuck.

Define a map type using json-schema v4

I'm trying to define a referenced schema for use as a Cassandra CQL map type with text fields. Specifically, I want to map URIs to strings.
Right now I have:
"scope": {
"type": "object",
"properties": {
"uri": {
"type": "string",
"format": "uri"
},
"permission": {
"type": "string",
"enum": ["read_only", "read_write", "write_only"]
}
},
"required": ["uri", "permission"],
"additionalProperties": false
}
This is good for data like
{"uri":"http://example.com",
"permission": "read_only"}
But I want a schema for data like
{"http://example.com": "read_only"}
http://spacetelescope.github.io/understanding-json-schema/reference/object.html has a solution:
{
"type": "object",
"patternProperties": {
"^S_": { "type": "string" },
"^I_": { "type": "integer" }
}
}
The problem with this is that I'd have to define a built-in format with a regular expression. Looking at examples of regexs for URIs makes me want to avoid this.
Since the number of URIs I have are limited, mapping enum to enum would also be a solution. Is that doable?
If I can be permitted to answer my own question, I believe the solution is to use a PatternProperties definition for the key, with a very specific regex. The value can be any type supported by json-schema, including another regex. In my case, it's an enum.
So the definition looks something like-
"patternProperties": {
"^https:\/\/www.example.com\/auth\/\\w+$": {
"type": "string",
"enum": ["read_only", "read_write", "write_only"]
}
},
"additionalProperties": false

How to tell JSON schema validator to pick schema from property value?

For example a schema for a file system, directory contains a list of files. The schema consists of the specification of file, next a sub type "image" and another one "text".
At the bottom there is the main directory schema. Directory has a property content which is an array of items that should be sub types of file.
Basically what I am looking for is a way to tell the validator to look up the value of a "$ref" from a property in the json object being validated.
Example json:
{
"name":"A directory",
"content":[
{
"fileType":"http://x.y.z/fs-schema.json#definitions/image",
"name":"an-image.png",
"width":1024,
"height":800
}
{
"fileType":"http://x.y.z/fs-schema.json#definitions/text",
"name":"readme.txt",
"lineCount":101
}
{
"fileType":"http://x.y.z/extended-fs-schema-video.json",
"name":"demo.mp4",
"hd":true
}
]
}
The "pseudo" Schema note that "image" and "text" definitions are included in the same schema but they might be defined elsewhere
{
"id": "http://x.y.z/fs-schema.json",
"definitions": {
"file": {
"type": "object",
"properties": {
"name": { "type": "string" },
"fileType": {
"type": "string",
"format": "uri"
}
}
},
"image": {
"allOf": [
{ "$ref": "#definitions/file" },
{
"properties": {
"width": { "type": "integer" },
"height": { "type": "integer"}
}
}
]
},
"text": {
"allOf": [
{ "$ref": "#definitions/file" },
{ "properties": { "lineCount": { "type": "integer"}}}
]
}
},
"type": "object",
"properties": {
"name": { "type": "string"},
"content": {
"type": "array",
"items": {
"allOf": [
{ "$ref": "#definitions/file" },
{ *"$refFromProperty"*: "fileType" } // the magic thing
]
}
}
}
}
The validation parts of JSON Schema alone cannot do this - it represents a fixed structure. What you want requires resolving/referencing schemas at validation-time.
However, you can express this using JSON Hyper-Schema, and a rel="describedby" link:
{
"title": "Directory entry",
"type": "object",
"properties": {
"fileType": {"type": "string", "format": "uri"}
},
"links": [{
"rel": "describedby",
"href": "{+fileType}"
}]
}
So here, it takes the value from "fileType" and uses it to calculate a link with relation "describedby" - which means "the schema at this location also describes the current data".
The problem is that most validators do not take any notice of any links (including "describedby" ones). You need to find a "hyper-validator" that does.
UPDATE: the tv4 library has added this as a feature
I think cloudfeet answer is a valid solution. You could also use the same approach described here.
You would have a file object type which could be "anyOf" all the subtypes you want to define. You would use an enum in order to be able to reference and validate against each of the subtypes.
If the sub-types schemas are in the same Json-Schema file you don't need to reference the uri explicitly with the "$ref". A correct draft4 validator will find the enum value and will try to validate against that "subschema" in the Json-Schema tree.
In draft5 (in progress) a "switch" statement has been proposed, which will allow to express alternatives in a more explicit way.