json

FormatValidateConvert

In this section · 03 Schemas for validators and modelsJSON Schema

03 · Schemas for validators and models

A JSON Schema, and checking the next file

Write a schema from one sample, then hold the next document to it in the editor.

Guide 7 of 8 in Generate

Input

product.json

{
  "sku": "PEN-01",
  "price": 2.5,
  "stock": 40,
  "tags": ["office"]
}

Do

  1. Set From to JSON, and under Language choose JSON Schema.
  2. In Options, under JSON Schema, set Draft to 2020-12, and leave allow extra properties unticked.
  3. Paste the input into the source pane, and type Product into Root name, since a paste has no file name to take it from.

Result

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$ref": "#/$defs/Product",
  "$defs": {
    "Product": {
      "type": "object",
      "properties": {
        "sku": {
          "type": "string"
        },
        "price": {
          "type": "number"
        },
        "stock": {
          "type": "integer"
        },
        "tags": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "required": [
        "sku",
        "price",
        "stock",
        "tags"
      ],
      "additionalProperties": false
    }
  }
}

Try it in Generate →

A type in a programming language is a promise to the compiler. A JSON Schema is a promise any program can check, in any language, at the moment the data arrives. That makes it the target to reach for when a file comes from outside — a partner’s export, a config someone else edits, the body of a request — and you want to reject a bad one before it goes any further. This page is part of the Generate section.

Writing a schema by hand is tedious, and the first draft is always the same list of types and required fields. So the quickest route is to generate that draft from a record you know to be good, as the example above does from one product, and then spend your effort on the rules a sample cannot show.

What the generated schema says

The product is defined once, under $defs, and the top of the schema refers to it with $ref, the same way a nested object in a larger sample would get a definition of its own. Each field has the type it held: stock is an integer because it held a whole number, while price is a number. The list of tags is an array of strings.

required lists every field the sample had. With one record that is all of them. Paste a list of records instead, and a field joins the list only when every record carried it. Last, "additionalProperties": false refuses any key the sample did not have, which is what catches a misspelt field name. When the data is allowed to grow, tick allow extra properties and that line goes.

Which draft

Draft offers 2020-12, the current version and the default, and draft-07, the one many older tools still read. For a generated schema the difference is small: draft-07 keeps its definitions under definitions instead of $defs, and the $schema line names the older draft. Choose draft-07 only when the tool that will read the schema asks for it.

The editor’s validator is built around draft-07, and says so: under the verdict for this schema it prints 2020-12, read as draft-07. That line is a statement of which rules ran, not a warning. Every keyword a generated schema uses means the same in both drafts and is checked in full, so a schema from this page gets a verdict of Valid or Invalid in the editor, never Not fully checked, whichever draft you pick.

Checking the next file

Left pane

product-2.json

{
  "sku": "PEN-02",
  "price": "3.10",
  "tags": []
}

Right pane

generated.schema.json

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$ref": "#/$defs/Product",
  "$defs": {
    "Product": {
      "type": "object",
      "properties": {
        "sku": {
          "type": "string"
        },
        "price": {
          "type": "number"
        },
        "stock": {
          "type": "integer"
        },
        "tags": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "required": [
        "sku",
        "price",
        "stock",
        "tags"
      ],
      "additionalProperties": false
    }
  }
}

Do

  1. In the editor band, under Mode, choose Schema.
  2. Paste the next product into the left pane, and the generated schema into the right.
  3. Read the verdict on the band, then open Validate for the list.

Result

Invalid · 2 errors

$.price  type  Expected a number, found a string.
$  required  Missing required property "stock".

Try it in the editor →

The right pane above is the schema from the first example, unchanged, and the left pane is the next product to arrive. Its price came through as a string, "3.10", and it has no stock at all. Each problem is reported on its own line with the path where it was found: the price at $.price, and the missing field at $, the object that should have held it.

The editor’s Generate panel does both steps in one press. Open the good record in the editor, press Generate in the band, choose the JSON Schema tab, and press Validate with this. The editor switches to Schema mode, puts the schema in the right pane as generated.schema.json, and opens the list of errors. One undo in the right pane takes it back. From then on, paste each new file into the left pane and the verdict updates as you do.

The Generate page has no such button, because it has no second pane to put the schema in. There, press Copy and paste the schema into the right pane of the editor in Schema mode yourself.

Strict for a config, open for a response

Whether the strict line belongs in the schema depends on where the file comes from. For a config it usually does: a service reads a fixed set of settings, and an unknown key there is most often a misspelling of a known one. Generate the schema from the copy that works, check a hand-edited copy against it, and a key the working copy never had is reported by name, with the mark on the key itself.

Read that error as a question rather than a verdict. Either the key is a typo for one that exists, or the program has grown a setting and the schema wants generating again from a newer sample. An API response gains fields over time without breaking anyone, so there the strict line mostly gives the second answer; tick allow extra properties before you press Copy.

Tightening it by hand

A generated schema only knows what one sample showed it. It cannot tell that stock should never be negative, that a SKU always starts with three capitals and a dash, or that a tag must be one of a handful of words. Those rules are yours to add — minimum, pattern and enum are the usual three — and the editor re-checks the left pane each time you change the right one, so every edit is tested at once against a real file.

For how Schema mode shows its verdicts and what each of the four means, see Why a JSON Schema check says “not fully checked”. For the validator itself — which keywords it supports and how it treats a large schema — the JSON Schema validator page has its own article. Open the editor to check a file of your own, or Open Generate to start a schema.