JSON to JSON Schema Generator

Paste a real payload and get a schema written from what the data shows. Pick the draft your validator speaks, decide how required gets settled, then check a second payload against the result before the schema reaches CI.

JSON Schema generation bench

Required policy
Inference
JSON sampleA list of records beats one record
Waiting for JSONCtrl + Enter regenerates
JSON SchemaEditable, the checker below reads this box
0 properties0 required0 levels deep0 formats

Property ledger

One row per path. The last column is the evidence the decision rests on, and every amber row is a spot where the sample left the generator guessing.

Paste JSON above and the ledger fills in.

Check a payload against this schema

Paste a second response, or one you expect to fail. Errors come back with a JSON Pointer to the offending value.

No check run yet.

Parsing, inference and the checker all run in this tab. Nothing is uploaded.

Writing a JSON Schema from a sample, and knowing where the sample lies

A schema written by hand drifts. The API grows a field, the schema does not, and the validator starts rejecting payloads nobody touched. Reading the schema off a real payload fixes the transcription problem, but a sample only shows what happened once, not what is allowed. The sections below cover what each JSON value turns into, why the four drafts differ in ways validators punish, how required gets decided, and which lines deserve a second look before the file goes near CI.

What each JSON value turns into

JSON has six value kinds and the schema vocabulary has a keyword for each. Strings become {"type": "string"}, with a format attached when the value matches a known pattern. A whole number becomes integer when the toggle is on, a decimal becomes number, and true or false becomes boolean. A bare null is typed null on its own, or joins the type list when the same key holds a real value somewhere else in the sample. Objects get properties and a required array. Arrays get an items schema merged from every entry in the list, which is the part most generators skip.

Take the two order lines from the loaded sample:

"items": [{ "sku": "TX-CABLE-2M", "qty": 2, "unit_price": 19.75, "gift": true },{ "sku": "TX-DOCK-USB4", "qty": 1, "unit_price": 110 }]

Both entries are folded into one item schema before anything is written. unit_price holds a decimal in one and a whole number in the other, so the merged type is number. gift appears in one record out of two, so under the default policy the key stays out of required:

"items": {"type": "array","items": {"type": "object","properties": {"sku": { "type": "string" },"qty": { "type": "integer" },"unit_price": { "type": "number" },"gift": { "type": "boolean" }},"required": ["sku", "qty", "unit_price"]}}

The ledger under the editors records both decisions with the counts behind them, so you know which lines came from evidence and which from a single occurrence.

Four drafts, and the differences a validator will punish

The $schema line is not decoration. Validators read the draft from it and switch keyword behaviour to match, so a schema written for one draft and declared as another fails in ways the error messages rarely explain. The generator only writes keywords the chosen draft defines:

One trap worth naming. OpenAPI 3.0 uses a subset of draft 05 with its own nullable: true keyword instead of "type": ["string", "null"]. A schema from this page with a null in the type list will fail OpenAPI 3.0 linting. OpenAPI 3.1 accepts the type list as written.

Required is a policy, and the sample cannot settle it for you

Nothing in a JSON document says which keys are mandatory. A key present in the payload was present that time. The three policies in the setup strip are three different answers to the same question:

Whichever policy is on, the ledger names the count behind each decision, such as gift present in 1 of 2 records. A required key with a low count is the line to question first.

integer against number, and 110 against 110.0

JSON has one numeric type. The schema vocabulary has two, and the gap between them causes most of the surprise rejections people bring to a schema. A price field holding 110 reads as an integer, and if every record in the sample happens to hold a whole number, the schema says integer and the first payload with 19.75 fails. The generator widens to number the moment a decimal shows up anywhere at the same path, and the ledger marks the row as widened so you know the evidence was mixed.

The reverse direction is the safer edit. A qty or a version field is meant to be whole, and integer catches a 1.5 that would otherwise slip through. Money is the case to think about: if the API sends cents as a whole number, keep integer. If it sends dollars with a decimal, switch the toggle off for that field or edit the type in the right hand box before running the check.

Formats are annotations until the validator is told otherwise

With detection on, a string matching one of eight patterns gets a format keyword: date-time, date, time, email, uuid, uri, ipv4 and ipv6. The pattern match is strict, so 2026-08-14T09:12:44Z is a date-time while an order reference like 2024-1234 is left alone. When strings at the same path disagree, no format is written and the ledger says how many matched.

What the keyword does at validation time depends on the validator. In draft 2019-09 and 2020-12, format is an annotation by default and validators are told to ignore it unless format assertion is switched on. Ajv refuses to compile a schema carrying format at all unless ajv-formats is installed. Python's jsonschema needs a FormatChecker passed in. The checker on this page does test formats, and labels those errors so you know they might pass elsewhere.

Nulls, empty arrays, and other holes in the evidence

Some values carry less information than they appear to. A key holding null in every record has no known real type, so the schema says null and the ledger flags the row amber. Fix it by pasting a sample where the field is filled, or by editing the type list by hand. An empty array tells the generator the key is a list and nothing else, so items is left off and any element shape will pass. A list mixing strings and numbers comes through as a type list, and a list mixing objects with scalars comes through as anyOf, which validates but is rarely what the API intends.

Locking additionalProperties, and when not to

"additionalProperties": false turns every object into a closed shape. Any key not listed under properties fails. For a config file your own code reads, this is the right default, since a typo in a key name becomes a validation error instead of a silently ignored setting. For a payload someone else sends, it is a time bomb. Third party APIs add fields without a version bump, and a closed schema turns each addition into an outage.

The toggle applies to every object in the tree at once. If you want the root closed and nested objects open, generate with the toggle on and delete the keyword from the nested blocks in the editor. Under draft 2019-09 and later, unevaluatedProperties does the same job while still allowing keys contributed by allOf branches, which matters once the schema starts composing other schemas.

Testing the schema before it ships

The checker at the bottom of the bench validates a second payload against whatever is in the schema box, including your edits. The failing sample button loads an order with five planted problems: a missing currency, a total sent as a string, a fractional qty, a filled in phone where the sample only ever showed null, and a coupon key the schema has never seen. Run it with the lock toggle off and the extra key passes, run it with the lock on and the checker reports it. The phone error is the null trap from the section above, caught before it reached production. That round trip is the fastest way to feel what each toggle does.

The checker covers type, properties, required, additionalProperties, items, enum, anyOf, and the eight formats above. It is not a full implementation of any draft. Pass the final schema through Ajv, Python's jsonschema, or your platform's validator of record before wiring it into a pipeline.

Where this stops

Nothing you paste leaves the page. Parsing, inference and the checker all run in JavaScript inside your browser, so a response carrying customer data or a bearer token stays on your machine. Load the page once, drop the connection, and the bench keeps working.

Questions about generating JSON Schema from JSON

Draft choice, required policy, number widening, formats, nulls, and what the schema does and does not promise.

Which draft should I pick?

Match the validator that will read the schema. Ajv 8 defaults to draft 07 and needs a separate entry point for 2020-12. OpenAPI 3.1 documents use 2020-12. Older Java validators and Swagger 2 tooling speak draft 04. If nothing downstream states a preference, draft 07 is the safe choice because almost every validator still handles it, and moving up to 2020-12 later is a one line change for a schema this simple, since the generator never emits tuple keywords, which is where the drafts diverge.

Why is a key required when some of my records skip it?

Check the required policy. Under Every key seen, a key that appeared in one record is required everywhere. Switch to Present in every record and the key drops out of the required array the moment one object at the same path omits it. The ledger shows the count behind each decision, such as present in 3 of 5 records, so a required key with a low count is easy to spot. If the policy is already Present in every record and the key is still required, every object in your sample carries it, and the sample needs a record without it.

Why did unit_price come out as number when my value is 110?

Because another value at the same path held a decimal. The generator profiles every occurrence of a key before writing the type, and a single 19.75 alongside a 110 widens the whole field to number, which is the honest reading. The ledger marks the row as widened with the counts. If the field is meant to be whole, the decimal is the record to investigate. If it is a price, number is right and the sample did its job.

Does the schema validate email and date-time strings?

Only if the validator is told to. The format keyword is an annotation by default in draft 2019-09 and 2020-12, and even in draft 07 many libraries skip it unless a format plugin is present. Ajv refuses to compile a schema with format unless ajv-formats is loaded. Python jsonschema needs a FormatChecker argument. The checker on this page does test the eight formats it detects, and labels those errors as format errors so you know they may pass silently elsewhere.

What happens to a field holding null?

If the field is null in every record, the schema types it null and the ledger flags the row, because the real type is unknown. If the field holds a value in some records and null in others, null joins the type list, so you get type: [string, null] and a validator accepts either. Draft 04 handles the type list fine. OpenAPI 3.0 does not, and wants nullable: true on a single type instead, so edit the schema box before pasting into a 3.0 document.

Can it write $ref and $defs for repeated objects?

No. Every object is written inline, so two blocks with the same shape appear twice. The output is easier to read and paste in pieces, and validators do not care. If the schema grows past a screen and the duplication bothers you, cut the repeated block into $defs by hand, which takes a minute, and the checker on this page will not follow the reference, so run that version through Ajv or jsonschema instead.

Is this the same thing as an OpenAPI schema object?

Close, and the distance depends on the OpenAPI version. OpenAPI 3.1 uses JSON Schema draft 2020-12 directly, so a schema from this page with that draft selected drops straight into a components.schemas entry, minus the $schema line. OpenAPI 3.0 uses its own dialect with nullable instead of null in the type list and no examples array, so a null bearing schema needs editing first. Swagger 2 is closer to draft 04.

Is my JSON uploaded anywhere?

No. Parsing, type inference, schema generation and the payload checker all run in JavaScript inside this page. Nothing is sent after the page loads, so an order carrying a customer email or a token never leaves your machine. Nothing is stored between visits either, and closing the tab clears every box.