How to define UUID property in JSON Schema and Open API (OAS) - jsonschema

When using JSON Schema and Open API specification (OAS) to document a REST API, how do I define the UUID property?

There's no built-in type for UUID, but the OpenAPI Specification suggests using
type: string
format: uuid
From the Data Types section (emphasis mine):
Primitives have an optional modifier property: format. OAS uses several known formats to define in fine detail the data type being used. However, to support documentation needs, the format property is an open string-valued property, and can have any value. Formats such as "email", "uuid", and so on, MAY be used even though undefined by this specification.
For example, Swagger Codegen maps format: uuid to System.Guid in C# or java.util.UUID in Java. Tools that don't support format: uuid will handle it as just type: string.

The only way I found so far is to manually specify the RegEx pattern as reusable schema component:
openapi: 3.0.1
paths:
/transactions/:
post:
responses:
200:
content:
application/json:
schema:
type: object
properties:
transactionId:
$ref: '#/components/schemas/uuid'
components:
schemas:
uuid:
type: string
pattern: '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
# the regex above limits the length;
# however, some tools might require explicit settings:
minLength: 36
maxLength: 36
But, I would definitely want to use a more standardized approach.

Since the question was originally asked the JSON Schema spec has been extended to provide built-in support for specifying and validating that a JSON field of type string is a UUID - specifically that it adheres to the format of a UUID as defined by RFC4122, e.g. “f81d4fae-7dec-11d0-a765-00a0c91e6bf6”.
The support was added in JSON Schema spec version 2019-09 (previously known as draft-08). The JSON Schema Validation component spec was extended such that the existing ‘format' keyword that can be specified for schema fields of type string now supports a new built-in format named "uuid".
The example JSON schema below declares a (mandatory) field named "id" of type string that must be formatted as UUID -
{
"$schema": "http://json-schema.org/draft/2019-09/schema#",
"title": "My JSON object schema",
"description": "Schema for the JSON representation of my JSON object.",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for my object. (A UUID specified by RFC4122).",
"type": "string",
"format": "uuid"
}
},
"required": ["id"]
}
Note that at the time of writing, the section of the JSON Schema user guide ("Understanding JSON Schema") covering examples of built-in string validation - JSON Schema Reference > Type-specific keywords > string > Format - doesn’t mention UUID supports, as it’s out of date - it currently only describes JSON Schema draft-7.
For the Java developers among you, the RFC4122 format used by JSON schema is compatible with the string representation of Java’s UUID class - it’s Javadoc also mentions RFC 4122.
For more details see -
The JSON Schema Validator spec section 7. A Vocabulary for Semantic Content With “format” > 7.3. Defined Formats > 7.3.5. Resource Identifiers - The official spec.
This GitHub issue https://github.com/json-schema-org/json-schema-spec/issues/542 (01/2018) requested that support be added. And the enhancement was duly implemented in 03/2019. See pull request https://github.com/json-schema-org/json-schema-spec/pull/715.

Related

JSON Schema validator compatibility

I'm trying to understand how a single JSON Schema behaves when used in different validators. Some validators define custom keywords. For example ajv validator ajv-keywords package defines a prohibited keyword that is not part of the JSON Schema standard. JSON Schema on the other hand defines the required keyword that would seem to be the polar opposite of prohibited. JSON Schema also defines a oneOf combinator that can be used to validate that the input should match one and only one of several schema definitions.
Consider the following schema example. By reading the json schema specification, I get the impression that the example json schema should validate any json object when used in ajv. However, according to the unknown keyword rules, validators are supposed to ignore any keywords they do not support. So, I imagine that another validator would ignore the custom prohibited keyword, causing the schema to reject an input with property foo. Is this correct or am I failing to read the json schema specification?
{
"oneOf": [
{
"type": "object",
"required": ["foo"]
},
{
"type": "object",
"prohibited": ["foo"]
}
]
}
You are correct. A standard JSON Schema validator will fail validation for an object that has a property "foo". You should be very careful using non-standard keywords if you expect your schemas to be used by standard validators.
It should be okay to use custom keywords as long as you follow the principle of progressive enhancement. Effectively, that means the behavior should degrade as gracefully as possible if the custom keyword is ignored. Your example violates this principle because you end up with a false negative result if prohibited is ignored.
An simple example that does follow progressive enhancement might look like this...
{
"type": "object",
"properties": {
"foo": {}
},
"required": ["foo"],
"prohibited": ["bar"]
}
If I run this through a standard validator, all assertions work as expected except prohibited which is ignored. Assuming a client-server architecture, this allows clients to mostly validate their requests before sending them to the server. The server then does it's own validation with a validator that understands the custom keywords and can respond with an error if "bar" is present.

Can I compose two JSON Schemas in a third one?

I want to describe the JSON my API will return using JSON Schema, referencing the schemas in my OpenAPI configuration file.
I will need to have a different schema for each API method. Let’s say I support GET /people and GET /people/{id}. I know how to define the schema of a "person" once and reference it in both /people and /people/{id} using $ref.
[EDIT: See a (hopefully) clearer example at the end of the post]
What I don’t get is how to define and reuse the structure of my response, that is:
{
"success": true,
"result" : [results]
}
or
{
"success": false,
"message": [string]
}
Using anyOf (both for the success/error format check, and for the results, referencing various schemas (people-multi.json, people-single.json), I can define a "root schema" api-response.json, and I can check the general validity of the JSON response, but it doesn’t allow me to check that the /people call returns an array of people and not a single person, for instance.
How can I define an api-method-people.json that would include the general structure of the response (from an external schema of course, to keep it DRY) and inject another schema in result?
EDIT: A more concrete example (hopefully presented in a clearer way)
I have two JSON schemas describing the response format of my two API methods: method-1.json and method-2.json.
I could define them like this (not a schema here, I’m too lazy):
method-1.json :
{
success: (boolean),
result: { id: (integer), name: (string) }
}
method-2.json :
{
success: (boolean),
result: [ (integer), (integer), ... ]
}
But I don’t want to repeat the structure (first level of the JSON), so I want to extract it in a response-base.json that would be somehow (?) referenced in both method-1.json and method-2.json, instead of defining the success and result properties for every method.
In short, I guess I want some kind of composition or inheritance, as opposed to inclusion (permitted by $ref).
So JSON Schema doesn’t allow this kind of composition, at least in a simple way or before draft 2019-09 (thanks #Relequestual!).
However, I managed to make it work in my case. I first separated the two main cases ("result" vs. "error") in two base schemas api-result.json and api-error.json. (If I want to return an error, I just point to the api-error.json schema.)
In the case of a proper API result, I define a schema for a given operation using allOf and $ref to extend the base result schema, and then redefine the result property:
{
"$schema: "…",
"$id": "…/api-result-get-people.json",
"allOf": [{ "$ref": "api-result.json" }],
"properties": {
"result": {
…
}
}
}
(Edit: I was previously using just $ref at the top level, but it doesn’t seem to work)
This way I can point to this api-result-get-people.json, and check the general structure (success key with a value of true, and a required result key) as well as the specific form of the result for this "get people" API method.

Multiple Response Body Examples depending on media type in OpenApi 3.0.0/Swagger

I am trying to document the response for an endpoint that accepts multiple mime type requests and returns a different response based on the type. One response type, pdf, returns its own schema separate from the others. The rest all return the same schema and same example. This syntax works. Almost. Except in the UI example, the JSON response is also showing the string "$ref: example-two.json" along with the appropriate response example. Like:
{
"key": "value",
"key2": "value2",
"$$ref: example-two.json"
}
when it should just be:
{
"key": "value",
"key2": "value2"
}
I've been scouring the docs and stack and google and I don't see any examples of making something like this work. Or rather, I'm not seeing why this one isn't working, but I haven't seen any examples that include $refs for each example.
responses:
200:
content:
application/pdf:
schema:
$ref: ../app.yaml#/components/schemas/ModelOne
application/json:
schema:
$ref: ../app.yaml#/components/schemas/ModelTwo
examples:
Example:
value:
$ref: example-two.json
text/html:
schema:
$ref: ../app.yaml#/components/schemas/ModelTwo
examples:
Example:
value:
$ref: example-two.json
For context - I cannot change the endpoint behavior and I do need to show an example for each mime type even if they are the same. Because the one is different. Thank you in advance!
There are two ways to $ref an example in OpenAPI 3.0:
1) Define the example in the components/examples section. In this case the $ref is used inside the examples.<name> key (not inside value).
examples:
Example:
$ref: '#/components/examples/MyExample'
...
components:
examples:
MyExample:
summary: Optional short description of this example
value:
key: value
key2: value2
2) If the example-two.json file contains just the example value (in this case - sample JSON), you can use externalValue to link to that file:
examples:
Example:
externalValue: example-two.json
Notes:
Relative URLs in externalValue are resolved against the API server URL (servers[*].url) and not the location of the OpenAPI definition file. You may need to use an absolute URL.
Examples with externalValue are currently (as of December 2019) not displayed in Swagger UI - see issue #5433.

Use of type : object and properties in JSON schema

I'm new to JSON.
I see in various examples of JSON, like the following, where complex values are prefixed with "type":"object", properties { }
{
"$schema": "http://json-schema.org/draft-06/schema#",
"motor" : {
"type" : "object",
"properties" : {
"class" : "string",
"voltage" : "number",
"amperage" : "number"
}
}
}
I have written JSON without type, object, and properties, like the following.
{
"$schema": "http://json-schema.org/draft-06/schema#",
"motor" : {
"class" : "string",
"voltage" : "number",
"amperage" : "number"
}
}
and submitted to an on-line JSON schema validator with no errors.
What is the purpose of type:object, properties { }? Is it optional?
Yes it is optional, try removing it and use your validator.
{
"$schema": "http://json-schema.org/draft-06/schema#",
"foo": "bar"
}
You actually don't even need to use the $schema keyword i.e. {} is valid json
I would start by understanding what json is, https://www.json.org/ is the best place to start but you may prefer something easier to read like https://www.w3schools.com/js/js_json_intro.asp.
A schema is just a template (or definition) to make sure you're producing valid json for the consumer
As an example let's say you have an application that parses some json and looks for a key named test_score and saves the value (the score) in a database in some table/column. For this example we'll call the table tests and the column score. Since a database column requires a type we'll choose a numeric type, i.e. integer for our score column.
A valid json example for this may look like
{
"test_score": 100
}
Following this example the application would parse the key test_score and save the value 100 to the tests.score database table/column.
But let's say a score is absent so you put in a string i.e "NA"
{
"test_score": "NA"
}
When the application attempts to save NA to the database it will error because NA is a string not an integer which the database expects.
If you put each of those examples into any online json validator they are valid json example. However, while it's valid json to use "NA" or 100 it is not valid for the actual application that needs to consume the json.
So now you may understand that the author of the json may wonder
What are the different valid types I can use as values for my test
score?
The responsibility then falls on the writers of the application to provide some sort of definition (i.e a schema) that the clients (authors) can reference so the author knows exactly how to structure the json so the application can process it accordingly. Having a schema also allows you to validate/test your json so you know it can be processed by the application without actually having to send your json through the application.
So putting it altogether let's say in the schema you see
"$test_score": {
"type": "integer",
"format": "tinyint"
},
The writer of the json now knows that they must pass an integer and the range is 0 to 255 because it's a tinyint. They no longer have to trial by error different values and see which ones the application process. This is a big benefit to having a schema.

Is it possible to add a custom keyword?

Using Json.NET schema, I am trying to create the simple schema:
{
"type": "object",
"customKeyWord": "customValue",
"properties": [
]
}
Is it possible to create a custom keyword at the top level like this?
Yes. Specs:
6.4. Extending JSON Schema
Implementations MAY define additional keywords to JSON Schema. Save for explicit agreement, schema authors SHALL NOT expect these additional keywords to be supported by peer implementations. Implementations SHOULD ignore keywords they do not support.