JSON Schema to Code in 2026: quicktype vs json-schema-to-typescript vs datamodel-codegen

Every team eventually writes the same file twice: once as a JSON Schema, and once as hand-typed models, which then drift apart until production accepts a payload the schema rejects. The fix is a generator — but the three most popular ones are not interchangeable. One emits 19 languages and infers schemas from samples, one emits TypeScript and nothing else, and one emits validated Python classes with $ref resolution that actually survives a real OpenAPI document.

Below is the comparison I wish I had before wiring one of these into CI, including the exact commands, the actual output shapes, and where each one silently fails.

TL;DR: The 60-Second Verdict

  • Polyglot repo (TypeScript + Go + Swift + Rust + Java in one tree): quicktype. v26.0.0, Apache-2.0, 13,910 stars, and it is the only tool here that can infer a schema from sample JSON and then emit models in 19 target languages from that same schema.
  • TypeScript-only, schema already committed, want a one-line build step: json-schema-to-typescript (json2ts). 3,353 stars, MIT, 1 dependency-light CLI, glob support, and it does not try to be a platform.
  • Python backend with Pydantic v2 models, OpenAPI/Avro/Protobuf mixed in: datamodel-code-generator. 4,056 stars, MIT, v0.92.0, and the only one of the three that handles allOf/oneOf/anyOf plus multi-format inputs without a wrapper script.

If you only read one line: quicktype for breadth, json2ts for TypeScript discipline, datamodel-codegen for Python correctness.

The Three at a Glance

GitHub numbers pulled 2026-11-10.

quicktypejson-schema-to-typescriptdatamodel-code-generator
Repoquicktype/quicktypebcherny/json-schema-to-typescriptkoxudaxi/datamodel-code-generator
Stars13,9103,3534,056
Last push2026-10-302026-09-072026-11-10
Latestv26.0.0npm-only (no GitHub releases)0.92.0
LicenseApache-2.0MITMIT
LanguageTypeScript (CLI)TypeScriptPython
Output languages19 (TS, Go, Rust, Swift, Java, Kotlin, C#, Dart, Ruby, Python, C++, Scala, PHP, Elm, Haskell, Objective-C, Flow, Pike, JSON Schema)TypeScript onlyPython only (Pydantic v2, dataclass, TypedDict, msgspec)
Input formatsJSON, JSON Schema, TypeScript, GraphQLJSON SchemaOpenAPI 3, AsyncAPI, JSON Schema, Avro, XSD, Protobuf/gRPC, GraphQL, raw JSON/YAML/CSV
Infers schema from samplesyesnono
$ref / compositiongoodgood for local refsstrongest (allOf, oneOf, anyOf, remote refs)
Web playgroundapp.quicktype.iolive demodocs playground (local, Pyodide)

Decision Matrix: Pick by Situation

Your situationPickReason
Monorepo with TS frontend and Go/Rust services sharing contractsquicktypeone schema, many --lang targets, byte-identical naming rules
Only TypeScript, schema lives in schemas/*.jsonjson2tsjson2ts -i 'schemas/**/*.json' -o types/ and you are finished
Python + FastAPI + Pydantic v2datamodel-codegengenerates validated models, not just type hints
You have sample payloads but no schema yetquicktype-l schema infers a reviewable schema first
You need Avro or Protobuf schemas as inputdatamodel-codegenexplicit multi-format support
You want zero new runtime dependenciesjson2tsoutput is pure .d.ts, no runtime library
You need JSON Schema output as an artifactquicktypeit is one of the target languages

quicktype — Inference First, Then Nineteen Languages

quicktype’s recommended workflow is not “schema in, code out”. It is sample in, schema out, review, then code out — which is the correct order for legacy APIs where nobody documented the contract:

1
2
3
4
5
6
7
8
9
# 1. Infer a JSON schema from a real payload
quicktype pokedex.json -l schema -o schema.json

# 2. Review and edit schema.json, commit it to the repo

# 3. Generate models from the committed schema, in as many languages as you need
quicktype -s schema schema.json -o src/ios/models.swift
quicktype -s schema schema.json -o src/android/Models.java
quicktype -s schema schema.json -o src/nodejs/Models.ts

Install is a single global npm package:

1
npm install -g quicktype

And the fast path for one-off conversions still works exactly as documented:

1
2
echo '{ "name": "David" }' | quicktype -l csharp
quicktype person.json -o Person.swift

The -s schema flag is the part people miss. Without it, quicktype infers from your input and you get a different result than the schema your backend validates against. Always generate from the schema, not from a sample, once a schema exists.

quicktype options panel showing per-language generation settings

Where it breaks: Type naming is inferred from property names and can produce awkward identifiers in languages with strict conventions. Output is idiomatic but opinionated — you will likely wrap it in a formatter (gofmt, rustfmt, prettier) as a post-step. And because it supports 19 targets, edge cases in exotic languages get fixed slower than TypeScript ones.

json-schema-to-typescript — The Boring, Correct TypeScript Answer

json2ts does one thing. Given this schema:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "title": "Example Schema",
  "type": "object",
  "properties": {
    "firstName": { "type": "string" },
    "lastName": { "type": "string" },
    "age": { "description": "Age in years", "type": "integer", "minimum": 0 },
    "hairColor": { "enum": ["black", "brown", "blue"], "type": "string" }
  },
  "additionalProperties": false,
  "required": ["firstName", "lastName"]
}

it produces exactly what you would have typed by hand:

1
2
3
4
5
6
7
8
9
export interface ExampleSchema {
  firstName: string;
  lastName: string;
  /**
   * Age in years
   */
  age?: number;
  hairColor?: 'black' | 'brown' | 'blue';
}

Note the details that make it trustworthy: required becomes optionality, the enum becomes a string literal union rather than string, and the description survives as a JSDoc comment. Those three behaviors are why teams keep it in a prebuild script for years.

CLI usage, straight from the README:

1
2
3
4
5
6
7
8
# local install, then npx
npx json2ts -i foo.json -o foo.d.ts

# globs and directory output
json2ts -i 'schemas/**/*.json' -o types/

# piping
cat foo.json | json2ts > foo.d.ts

That glob form is the whole build integration for most projects — one line in package.json:

1
2
3
4
5
6
{
  "scripts": {
    "types:schemas": "json2ts -i 'schemas/**/*.json' -o src/types/",
    "prebuild": "npm run types:schemas"
  }
}

Where it breaks: it is TypeScript-only, by design. There are no GitHub releases (npm versions are the source of truth), so pin the exact npm version in package.json rather than a range. And it does not infer schemas — if all you have is sample payloads, you need quicktype or a hand-written schema first.

datamodel-code-generator — Python Models That Actually Validate

For Python the question is not “types” but “validation”. A TypedDict gives you editor completion; a Pydantic model rejects a payload at the boundary. datamodel-codegen targets the latter, and it is the only tool here that consumes OpenAPI, AsyncAPI, Avro, XSD, Protobuf and GraphQL as inputs.

Install it as an isolated CLI tool rather than a project dependency:

1
uv tool install datamodel-code-generator

Then generate from a JSON Schema:

1
2
3
4
5
6
datamodel-codegen \
  --input schema.json \
  --input-file-type jsonschema \
  --output-model-type pydantic_v2.BaseModel \
  --preset standard-py312-20261106 \
  --output model.py

The --preset flag is a 2026-era addition: presets bundle a coherent set of options for a target Python version, so you stop hand-maintaining twenty flags. --output-model-type lets you pick pydantic_v2.BaseModel, pydantic_v2.dataclass, dataclasses.dataclass, typing.TypedDict, or msgspec.Struct from the same schema.

The generated header is explicit about provenance, which matters when a reviewer asks why a file has no owner:

1
2
3
# generated by datamodel-codegen:
#   filename:  schema.json
#   timestamp: 2026-11-10T00:00:00+00:00

Where it breaks: it is Python-only, and the flag surface is large enough that two teams will produce different output from the same schema unless the invocation is committed to the repo. It also regenerates by overwriting files, so directories under generation should never be hand-edited — treat them as build artifacts.

The Pipeline Pattern That Actually Holds Up

After running all three, the pattern that survives contact with real teams is: schema is the source of truth, generation is a build step, generated files are committed or discarded consistently, and CI fails when regeneration produces a diff.

1
2
3
# CI drift check — fails if committed output is stale
quicktype -s schema schema.json -o /tmp/check.ts
diff -q /tmp/check.ts src/nodejs/Models.ts || (echo "schema drift: regenerate models"; exit 1)

That single check catches the most expensive class of bug in schema-driven codebases: a schema PR merged without regenerating the models, so the runtime validates one shape and the type system believes another.

If your schemas are OpenAPI documents rather than standalone JSON Schema, our OpenAPI generator comparison covers the server-stub side of the same problem. And if you are choosing a validator for the generated models at runtime, JSON Schema validation with ajv, prism and joi is the companion read. On the JVM specifically, see Java JSON Schema validators.

Pitfalls That Cost Real Debugging Time

1. Generating from samples instead of schemas. quicktype will happily infer a schema from one payload and mark every field required. Commit the inferred schema, edit it, then generate from the schema with -s schema.

2. Version pinning drift. quicktype is at v26.0.0, datamodel-codegen at 0.92.0, and json2ts publishes only to npm. Pin exact versions; generated code changing because a minor release changed naming rules is a genuinely miserable diff to review.

3. Committing generated files without a drift check. Either generate in CI and never commit, or commit and diff. Doing neither means the two copies diverge within a sprint.

4. Ignoring additionalProperties. json2ts defaults to closed objects only when the schema says so; a schema with additionalProperties: true yields an index signature, which quietly weakens every downstream type.

5. Forgetting that $ref resolution differs. Remote refs work in some tools and not others. Vendor your schemas locally before generating, or your build breaks the day the upstream URL changes.

6. Using enum values as bare strings. All three tools can emit literal unions or Python Enum classes. Prefer them — a string field with an enum constraint is a runtime failure waiting to be shipped.

FAQ

Which JSON Schema code generator is fastest to set up?

json-schema-to-typescript. One npx json2ts -i schema.json -o types.d.ts and you have output, with no configuration file and no runtime dependency. quicktype needs a global install and a decision about --lang; datamodel-codegen needs --input-file-type, --output-model-type, and ideally a --preset.

Can quicktype generate Python models from JSON Schema?

Yes. quicktype -s schema schema.json -l python -o models.py. It produces typed classes, but it does not produce Pydantic validators. If you need boundary validation rather than type hints, datamodel-code-generator’s pydantic_v2.BaseModel output is the correct choice.

Does json-schema-to-typescript support $ref and definitions?

Yes, for local references. It resolves definitions/$defs and emits shared interfaces. It is also the tool of the three with the narrowest scope, so what it supports it tends to support correctly, including allOf composition and oneOf unions.

How do I keep generated models in sync with the schema?

Run generation in CI and diff the result against the committed output. If the diff is non-empty, fail the build with a message telling the author to regenerate. This is three lines of shell and it eliminates the entire class of stale-contract bugs.

Is datamodel-code-generator only for OpenAPI?

No. It accepts OpenAPI 3, AsyncAPI, JSON Schema, Avro, XML Schema, Protobuf/gRPC, GraphQL and raw JSON/YAML/CSV, and can also retarget existing Python classes with --input-model path/to/file.py:ClassName. That breadth is why it is the default in Python codebases that consume more than one schema dialect.

Do these tools work offline?

Yes, all three are local CLI tools with no required network access, and quicktype’s web app is documented to work offline without sending sample data anywhere. Only remote $ref resolution requires network access, which is another reason to vendor schemas into the repo.


💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com