Chris Collins

Tools · dev

JSON → types

Paste JSON samples (or a JSON Schema) and get TypeScript, Zod, Java records with Jackson (or Lombok classes), Kotlin, Go structs, Rust serde, Python pydantic or dataclasses, protobuf, or JSON Schema — merging samples to find optional fields, nulls, unions, enums, dates, UUIDs, maps and tagged unions.

Added

Works with

  • JSON workbench DataMake types in the JSON workbench sends its document here, and Open in JSON workbench takes the input back
  • Data converter DataMore type options… in the data converter sends its input here as JSON
How to use it

Paste a response from an API, a log line or a config file and get the types to read it with. The more samples you give, the better the types: a key missing from one sample becomes optional, a null makes a field nullable, a number in one sample and a string in another makes a union. It also takes a JSON Schema and writes the same types from that.

The data converter has a quick JSON → TypeScript / Go / Rust / Python of a single document; this tool is the thorough version. To run the TypeScript it writes, use the TypeScript playground.

How to use it

Paste on the left, or Open file…. The input can be one document, an array of records, JSON Lines (one document per line, as in logs and kubectl … -o json streams), several documents one after another, or a JSON Schema (draft-07 to 2020-12, and OpenAPI’s nullable). Auto decides which; Samples / JSON Schema force it. The Examples… show each feature.

What it works out, listed under What it found:

  • Optional fields — present in some objects at a position but not all — and nullable ones — null in at least one sample. Every sample counts, including every element of every array.

  • Unions when a field has several kinds (a number in one place, a string in another).

  • String formats: RFC 3339 date-times (with a zone, so Go’s time.Time and Java’s OffsetDateTime can parse them), dates, times, UUIDs, emails, URLs, IPv4 addresses.

  • Enums: a string field with a few values (up to 8, configurable) that repeat and look like identifiers — "active", "disabled" — not names or free text.

  • Maps: an object whose keys are data (numeric ids, UUIDs, dates) becomes a map, and {} a free-form object, rather than a type per key.

  • Tagged unions: objects with a string type, kind, event, op… where the other fields depend on it become a discriminated union — TypeScript | with literal tags, z.discriminatedUnion, a Jackson @JsonTypeInfo sealed interface, a kotlinx sealed interface, a serde #[serde(tag = "type")] enum, a pydantic Field(discriminator=…) union. Go and protobuf have no sum types, so there it is one struct with every variant’s fields and the tag.

  • Names: nested types are named after their keys (address → Address; array items are made singular: categories → Category); identical shapes share one type; a different shape with a taken name gets the parent’s name in front (OwnerAddress). Set the root name and a prefix or suffix (Dto).

JavaScript reads 1.0 as 1, so a float field whose samples are all whole numbers looks like an integer; tick Numbers as floats, or include a sample with a fraction.

Each language follows its own conventions, with the JSON names kept where they differ:

  • TypeScript — interfaces or type aliases, key? or key?: T | undefined (for exactOptionalPropertyTypes), string-union or enum enums, readonly.

  • Zod — schemas plus z.infer types; recursive schemas get z.lazy and written-out types. Works with Zod 3 and 4.

  • Java — records (Java 17+) or Lombok @Data @Builder classes, with Jackson annotations; OffsetDateTime, LocalDate and UUID for formats (or plain strings); boxed types for nullable and optional fields; one file per type.

  • Kotlin — @Serializable data classes for kotlinx.serialization, @SerialName where names differ, nullable types with = null defaults.

  • Go — structs with json:"…" tags, omitempty on optional fields, pointers for nullable ones (or for optional too, or never), Go initialisms (ID, URL, UUID), time.Time, string enums with constants, and `gofmt’s alignment.

  • Rust — serde structs, [serde(rename_all = "camelCase")] when one rule fits every field (else per-field rename), Option<T> with skip_serializing_if, [serde(untagged)] enums for unions, Box for recursion, raw identifiers for keywords (r#type).

  • Python — pydantic v2 models with Field(alias=…) (snake_case attributes, JSON names kept), or dataclasses with required fields first.

  • Protobuf — proto3 messages with optional, repeated, map<…>, json_name where the JSON name is not the proto default, and google.protobuf.Value/ListValue for what proto cannot say directly (unions, arrays of arrays).

  • JSON Schema — draft 2020-12 with $defs, required, formats and oneOf for tagged unions.

Copy or Download the result. Share link puts the input and settings in the URL.

Checked with the real compilers

While building this I generated every language for five test inputs (an array of users, JSON Lines events with a type tag, awkward keys and mixed values, a GitHub-style API response, and a JSON Schema with $ref, recursion and a oneOf) and checked the output with the real tools: TypeScript 6 tsc --strict --exactOptionalPropertyTypes with each sample assigned to the type; Zod 3 and 4 parsing each sample; javac with Jackson reading each sample into the records and the Lombok classes; gofmt, go vet and encoding/json decoding each sample with unknown fields disallowed; cargo with serde decoding each sample; pydantic validating each sample; protoc; and ajv validating each sample against the generated schema. Kotlin was not compiled.

Working with other tools

The JSON workbench has a Make types button that sends its document here; Open in JSON workbench here sends the input (or, on the JSON Schema tab, the generated schema) back there to format, query or diff. In the data converter, when the output is a type language, More type options… converts the input (YAML, CSV, XML, TOML…) to JSON and opens it here.

Privacy

Everything runs in your browser; nothing you paste is sent anywhere. The last input and your settings are remembered in this browser’s storage. A share link carries the input in the URL.

Runs entirely in your browser; nothing you type leaves this page.