Skip to main content

From a JSON Sample to a Typed JSON Schema in Five Seconds

Turn one real JSON object into a typed JSON Schema. See how inference assigns types, marks required fields, handles enums, and validates API payloads and configs.

Published By Li Lei
#json-schema #json #api-validation #developer-tools #openapi

From a JSON Sample to a Typed JSON Schema in Five Seconds

Most people meet JSON Schema the hard way: a validation error fires in CI, they open the schema file to fix it, and they find a wall of nested braces nobody enjoys writing. The irony is that you usually already have the answer sitting in front of you. The API response you just logged, the config file you inherited, the webhook payload you pasted into a scratch buffer, each one is a perfect description of the shape you want to validate. You just need something to read that shape and write it down in the right vocabulary.

That is the whole job of the JSON Schema Generator: you paste one real JSON sample, it walks the value, and it hands back a draft-07 or 2020-12 schema with every field typed and the required list filled in. No upload, no account, no blank file to stare at. This post explains what the tool reads off your data, what a schema buys you once you have it, and how to wire it into real validation.

What inference actually reads off your data

The tool does not guess. It parses your JSON and then walks the value recursively, and every node maps to a JSON Schema construct one to one. There are three rules worth knowing because they explain almost everything the output does:

  • Each leaf value gets a type. A string becomes type: string, true becomes type: boolean, null becomes type: null. Numbers split: a whole value like 10 infers integer, while 10.5 infers number. That distinction matters later, so keep it in mind.
  • Every property seen in an object is listed as required. From a single sample, the safest assumption is that every key you observed is mandatory, so each object's required array lists all its keys. You can toggle this off if your data has optional fields, which I cover below.
  • Nested objects get their own schema. A three-level nested object produces a three-level nested schema. Objects become type: object with a properties map, arrays become type: array with a merged items schema, and the recursion repeats all the way down.

Arrays get one extra trick. A homogeneous array like [1, 2, 3] collapses to a single items schema of type: integer. A mixed array like [1, "two", true] cannot use one type, so the tool emits items as an anyOf listing each distinct element schema. That anyOf is also how you express a small set of allowed shapes, the same spirit as an enum: you constrain the data to a known list of valid forms instead of accepting anything.

A worked example

Here is a small order object, the kind you might pull straight from a logged response:

{
  "id": 42,
  "customer": "Lei",
  "paid": true,
  "items": ["pen", "notebook"]
}

Paste it in and you get back, on draft-07:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "id": { "type": "integer" },
    "customer": { "type": "string" },
    "paid": { "type": "boolean" },
    "items": {
      "type": "array",
      "items": { "type": "string" }
    }
  },
  "required": ["id", "customer", "paid", "items"]
}

Read it against the three rules. id is 42, a whole number, so it typed as integer, not number. customer and paid came out string and boolean. The items array was all strings, so it collapsed to one items schema of type: string rather than spelling out two entries. And because every key was present in the sample, all four landed in required. Switch the draft toggle to 2020-12 and only the $schema line changes to https://json-schema.org/draft/2020-12/schema; the body is portable between both versions.

What a schema gives you

Once you have that schema, it stops being documentation and starts being a contract. Three things come for free.

Types. A validator now rejects "id": "42" arriving as a string when your handler expects an integer. That single mismatch is behind a surprising share of "it works on my machine" bugs, where one service serializes a number as text and the consumer quietly coerces it until the day it does not.

Required. The required array turns missing fields from a silent undefined into a loud validation failure. If your code reads payload.customer and the field never arrived, you would rather find out at the boundary than three function calls deep.

Enums and allowed shapes. When a field can only be one of a known set, an enum (or the anyOf the tool generates for mixed arrays) pins it down. A status field that should be "open", "closed", or "pending" rejects a typo like "opne" instead of letting it flow into a switch that silently does nothing.

Using it to validate API payloads and configs

The fastest payoff is config validation. Say you inherit a settings.json with thirty keys and zero guardrails. Drop one valid config into the generator, take the schema, and wire it into ajv or a CI check. Now a typo in a boolean flag fails the build instead of crashing in production at 3 am. The required toggle lets you mark every key mandatory or leave them optional in a single click, so you decide how strict the gate is.

For APIs, the same schema slots straight under components/schemas in an OpenAPI spec, or into a request-body validator at your route boundary. And for third-party webhooks, you can freeze today's shape: generate a schema from a current sample, commit it, and the next time the provider renames a field your validator flags the drift before it reaches your handler.

I did exactly this last month with a vendor webhook that had no documentation at all. I had one captured payload, pasted it in, got the schema, and committed it as the source of truth. Two weeks later the vendor added a field and dropped another; my CI check went red on the next sample, and I knew the shape had moved before any data hit production. That is a five-second tool standing in for a contract test I would never have written by hand.

A few honest caveats

One sample is one sample. If other records carry an optional field your sample lacked, the generated required list will be too strict, so feed a representative array or relax required afterward. The tool also infers structural types, not semantic string formats: a timestamp comes out as type: string, and you add format: date-time by hand where your validator needs it. Finally, remember the integer-versus-number split; if a field can hold decimals, make sure your sample actually shows one.

If you want to keep working with the data after, the schema pairs naturally with the other JSON tools here. Tidy a messy sample first with the JSON Formatter, or hand the same payload to the JSON to YAML converter when your config lives in YAML rather than JSON. The schema describes the shape; those tools reshape the data itself.

A schema you generate in five seconds is not a finished spec, but it is a working draft that matches your real data exactly, because it was read off that data instead of guessed. That beats a blank file every time.


Made by Toolora · Updated 2026-06-13