JSON Schema Generator

Paste or upload a JSON sample to draft a schema from it. Runs in your browser.

Pasted text and chosen files stay on this device. Nothing is sent to a server.

Drop a file here, choose one, or paste.

No output yet Generate to see the result here.

How to use it

  1. 1

    Paste a representative sample.

  2. 2

    Read the inferred-hint list and the required list — those are the two places the schema is claiming more than the sample shows.

What it does

A schema is a set of constraints, and the constraints a sample cannot support are the interesting ones. The two that bite are `required` — which the sample always supports and rarely justifies — and `format`, which is a guess about meaning made from a string's shape.

Walk the sample and emit a schema. Repeated shapes are hoisted into $defs and referenced. Any format keyword is emitted only with a companion note that it was inferred, and every inferred constraint is counted.

Why it works this way

`required` is the constraint a sample can never justify: every key was present because the sample is one document. It is emitted, because a schema needs one, and it is counted so the list can be reviewed rather than trusted.

`format` is a guess about meaning made from a shape. A string that looks like a date is a date only if the producer meant one, and a schema that asserts `format: date-time` will reject a payload that is perfectly valid JSON. So the hint is emitted with a note, never silently.

A shape that repeats is hoisted into `$defs` and referenced. Inlining it means two copies that can drift apart, which is the defect the reference exists to prevent.

`additionalProperties: false` is not the default here. A sample cannot show that a producer will never add a key, and forbidding extras turns a compatible change into a validation failure.

Examples

{"a": 1}
{"type": "object", "properties": {"a": {"type": "integer"}}, "required": ["a"]}
the type is supported by the sample; required is an assumption and is counted
{"created": "2026-11-07T00:00:00Z"}
{"type": "string", "format": "date-time"}  ← format inferred from the shape, not from the producer
a date-looking string is still a string; the format is a guess and is marked as one
{"a": {"x": 1}, "b": {"x": 2}}
{"$defs": {"shape1": {"type": "object", "properties": {"x": {"type": "integer"}}}}, "properties": {"a": {"$ref": "#/$defs/shape1"}, "b": {"$ref": "#/$defs/shape1"}}}
the repeated shape exists once, so the two uses cannot drift

Inputs and outputs

  • JSON sample
  • draft: 2020-12 / draft-07
  • additionalProperties: allow / forbid
  • format hints: infer from shape / off
  • JSON Schema
  • counts: required keys, $ref hoists, inferred format hints

Edge cases

  • an empty array — items cannot be inferred
  • a repeated shape — $ref rather than two copies
  • a string that looks like an email or a URI
  • mixed-type arrays — anyOf
  • integers vs numbers
  • a key whose value is sometimes an object and sometimes a string
  • a sample that does not validate against its own generated schema — a generator bug

Frequently asked questions

Why is everything in required?

Because every key was present in the one sample you gave. That list is a claim about your producer, not about the sample, so it is counted and meant to be reviewed.

Where did format come from?

From the shape of the string, which is a guess about meaning. Each one is listed so you can delete the ones your producer does not guarantee.

Why not additionalProperties: false?

A sample cannot show that no key will ever be added, and forbidding extras turns a compatible change into a failure.

Related tools

Sources

  • JSON Schema 2020-12: Validation (required, additionalProperties, format-annotation)