Skip to main content

JSON Schema Generator

A validated contract, inferred from real data.

Paste a sample document and Jsonviewpro infers a JSON Schema: types for every property, required fields, merged array item schemas and detected string formats such as date-time, email and uuid.

Input JSON

Size: 0 BLines: 0

JSON Schema

Paste valid JSON to see the output.
Size: 0 BLines: 0

Inference gives you a draft, not a contract

A JSON Schema is a specification: it says what a document is *allowed* to be. Generating one from a sample inverts that — it describes what one document happens to be, and assumes the rest look the same. The output is genuinely useful, and it is a starting point rather than an answer.

The distinction matters because the parts inference gets right are the tedious parts, and the parts it cannot know are the parts that catch real bugs. Knowing which is which is most of the skill.

What is inferred

From the dataInto the schema
Every valueA type — object, array, string, number, integer, boolean or null.
Whole numbersinteger rather than number. Mixed evidence widens to number.
Object keysproperties, plus required.
Array itemsA single items schema, merged across everything sampled.
String patternsA format — see below.

Seven string formats are recognised by shape: date-time, date, time, email, uuid, ipv4 and uri. Detection is regex-based and deliberately conservative, so a value that merely resembles a date is left as a plain string rather than constrained wrongly.

Sample in, schema out
"order_id": "a3f1c2d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d"
"placed_at": "2024-03-11T09:24:00Z"
"email": "sarah@example.com"
"total": 41.5
"qty": 2

"order_id": { "type": "string", "format": "uuid" }
"placed_at": { "type": "string", "format": "date-time" }
"email":     { "type": "string", "format": "email" }
"total":     { "type": "number" }
"qty":       { "type": "integer" }

How `required` is decided

For a plain object, every key present is required — there is no evidence to suggest otherwise. For an array of records the answer is more interesting: the schemas of the items are merged, and a property stays required only if it was present in every sampled item.

"items": [
  { "sku": "A-1", "qty": 2, "price": 9.5, "gift_note": null },
  { "sku": "B-7", "qty": 1, "price": 22.5 }
]

"items": {
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "sku": { "type": "string" },
      "qty": { "type": "integer" },
      "price": { "type": "number" },
      "gift_note": { "type": "null" }
    },
    "required": ["price", "qty", "sku"]
  }
}

gift_note appears in properties but not in required, because the second record does not have it. That intersection is the single most valuable thing the generator does — and it only works if your sample contains records that genuinely differ. Required lists are sorted alphabetically so schemas generated from the same data always match.

That behaviour is the Present in every sample setting, which is the default. Switching to All properties marks everything seen anywhere as required — occasionally what you want when you know the sample is complete and would rather fail loudly on a missing field than accept one.

Merging values that disagree

When one field holds different types across records, the merged schema accepts both. The most common case by far is a nullable field.

{ "note": null }  +  { "note": "hi" }

"note": { "type": ["null", "string"] }

An integer and a number merge to number. An object merged with null keeps its properties and gains "null" in its type list. Arrays merge their item schemas recursively, so an array of objects produces one item schema describing all of them. Up to 2,000 array items are sampled — well past the point where the shape stops changing.

Choosing a draft

DraftUse it when
2020-12The current standard, and the default. Use it unless something downstream forces otherwise.
2019-09Tooling that has not caught up with 2020-12.
Draft 07The most widely implemented draft in the wild — many validators and IDEs target it.
Draft 04Legacy tooling, and OpenAPI 2.0 / Swagger.

Draft 04 predates the null type being usable the way later drafts use it, so a nullable field is emitted differently and automatically:

2020-12   "note": { "type": ["null", "string"] }
draft-04  "note": { "type": "string", "nullable": true }

What inference can never know

These are the constraints worth adding by hand. Every one of them catches bugs that a generated schema will not.

  • Enums. A status field that was "active" in all five records is typed string, not enum: ["active", "archived"]. Only you know the full set.
  • Ranges. minimum, maximum, minLength, maxLength — a sample cannot tell a legal bound from the largest value that happened to occur.
  • Patterns. A regex for an SKU or an account number.
  • Uniqueness. uniqueItems, and identifier uniqueness across records — the analyzer will tell you whether it currently holds.
  • Cross-field rules. "shipped_at must be present when status is shipped" needs if/then or dependentRequired, written by a person.
  • Strictness. additionalProperties: false rejects any property the schema does not name. That is a decision about how your API is versioned rather than a fact about the data, so it is never inferred — add it by hand to the objects where you want it enforced.

Frequently asked questions

Which drafts are supported?
Draft 2020-12 (the default), 2019-09, draft-07 and draft-04. Nullable fields are downgraded automatically for draft-04, which cannot express a null type the way later drafts do.
How are required fields decided?
For a plain object, every key present is required. For an array of records, item schemas are merged and a property stays required only if it appeared in every sampled item — so a sample with genuinely varied records produces a far more accurate result.
Which string formats are detected?
date-time, date, time, email, uuid, ipv4 and uri, matched by shape. Detection is conservative: a value that only loosely resembles one is left as a plain string.
Will the schema include enums, ranges or patterns?
No, and it should not. A sample cannot distinguish "the only value that occurred" from "the only value allowed". Add those constraints yourself — they are where a schema earns its keep.
Can I validate a document against the generated schema?
Yes. Copy it into the JSON validator, which checks a document against a schema and reports each violation with its path.
What does `additionalProperties: false` do?
It rejects any property the schema does not name. Inference never adds it, because how strictly your API refuses unknown fields is a versioning policy rather than something a sample can reveal. Add it yourself to the objects where you want it enforced.