JSON to Code
Turn a sample response into types for TypeScript, Go, Python, Rust, C#, Kotlin or JSON Schema
The JSON
What to make
The types
Paste a response and get types out. Seven targets, and none of them needs the JSON to leave your machine, which matters when the sample you have is a real response with a real customer in it.
Every element of an array gets read
[
{ "id": 1, "name": "Ana" },
{ "id": 2, "name": "Ben", "note": "vip" }
]
Reading only the first element:
interface Item { id: number; name: string }
Reading all of them:
interface Item { id: number; name: string; note?: string }
The second one is right, and it is the reason
this looks at every element.This is the whole difficulty. One object is a fact; an array of objects is a sample, and the first element of it is not the schema. A list of a hundred orders where two have a discount field means that field is optional, and you only find that out by looking at all hundred.
So every element is merged. A field missing from any object becomes optional, a field that is a number in some and a string in others becomes a union, and the resulting type describes the whole sample instead of its first row.
Which means a bigger sample gives a better type. If you can paste a hundred records instead of one, do.
Null is not a type
"cancelled_at": null
That field is a date. Or a string. Or a number.
The sample cannot tell you, because null is not
a type, it is the absence of one.
So it comes out as null, and there is a switch
to call it unknown instead. Neither is a guess
dressed up as a fact.A field that is null in your sample could be anything when it is not null. Guessing that it is a string because the name ends in _at would be a guess presented as a fact, so this does not do it.
A field that is null in some records and a number in others is different: that one is known to be a nullable number, and comes out asnumber | null, *int64, Optional[int] or whatever the target language calls it.
Whole numbers and decimals
[{ "price": 10 }, { "price": 9.99 }]
price: number TypeScript, one type
price: float64 Go, because one decimal
anywhere makes the column
a decimal
A single whole number in the sample would have
given int64, and that would have been wrong.TypeScript has one number type so this never comes up. Go, Rust, C#, Python and Kotlin all distinguish, and a field that arrives as 10 in the sample and 9.99 in production will fail to parse if it was typed as an integer.
The rule is that one decimal anywhere in a field makes the whole field a decimal. It is the safe direction, and another reason a bigger sample helps.
Empty arrays
"tags": []
An empty array says nothing about what goes in
it. TypeScript gets unknown[], Go gets
[]interface{}. Neither is useful, and inventing
a type would be worse.There is nothing to infer from an empty list. The output says so instead of picking a type at random, which means you get a compiler error at the right place instead of a runtime one somewhere else.
Naming
Nested objects are named after the key they sit under, and objects inside an array take the singular of it, so users holds aUser. Two different shapes wanting the same name get a number each. Merging them would produce one type that fits neither.
Two identical shapes wanting the same name do share it, which keeps the output short when the same object appears in several places.
What each target gets right
Go uses a pointer for anything optional, because that is the only way to tell a missing field from a zero value, and adds omitempty to the tag. Python puts fields with defaults last, since it will not define the class otherwise. Rust and C# add a rename attribute only when the JSON name and the idiomatic name actually differ. Kotlin gets a default of null on optional fields so the constructor still works.
JSON Schema is draft 2020-12, with a required list instead of a mark on each field, additionalProperties set to false, and nullable fields written as a two entry type list.
What it will not do
It cannot know about anything absent from your sample: a field that is always present in the sample but optional in reality comes out required, and an enum looks like a plain string. Types generated from a sample are a starting point and want reading before they go into anything that matters.
It also cannot know a date from a string, because JSON has no date type. Every timestamp comes out as a string, which is what it is on the wire.