JSON to Go Struct
Generate idiomatic Go structs with correct json tags and initialisms from sample JSON — entirely on this device.
UserID and AvatarURL, not UserId — the Go wiki table.
The generated Go types appear here as you type.
Press ? for shortcuts
Do it offline:$ npx quicktype sample.json --lang go --just-types --top-level Root -o types.go
How to use it
- Paste an API response or drop a .json file; the struct pane fills in as you type.
- Use Add sample for more payloads from the same endpoint — a key absent anywhere turns optional.
- Tune the Go side: type name, nesting, pointers or zero values, omitempty, numeric type, any versus interface{}, initialisms.
- Copy the declarations, download a .go file, or share a permalink carrying samples and options.
Paste sample JSON and get Go declarations ready to drop into a package: one struct per object shape, tab-aligned the way gofmt leaves them, a json tag pinning every wire key byte-for-byte. Field names respect Go's initialisms — UserID and AvatarURL, never UserId. Feed several payloads and fields missing from any of them become pointers with omitempty. Everything runs in this tab; samples are never uploaded.
Examples
{"user_id": 42, "avatar_url": "https://example.com/a.png", "plan": {"renews_at": null}}type Root struct {
UserID int64 `json:"user_id"`
AvatarURL string `json:"avatar_url"`
Plan Plan `json:"plan"`
}
type Plan struct {
RenewsAt any `json:"renews_at"`
}
user_id and avatar_url come back as UserID and AvatarURL — the casing Go's Code Review Comments call for, where plain camel-casing yields UserId — and the nested object becomes a named Plan struct. renews_at is null here, so it stays any plus a chip asking for a real value.
{"user_id": 42, "nickname": "zed"}
{"user_id": 7}type Root struct {
UserID int64 `json:"user_id"`
Nickname *string `json:"nickname,omitempty"`
}
nickname is missing from the second sample, so pointer mode emits *string plus omitempty: nil means the key never arrived. Under zero values the same field prints Nickname string `json:"nickname,omitempty"` — shorter, but absent and empty collapse.
{"value": 9223372036854775807}type Root struct {
Value int64 `json:"value"`
}
That value is int64's exact maximum. Converters built on JSON.parse print it as 9223372036854776000; the scanner here reads the raw token, keeps int64 and raises the warning quoted below. The JSON Formatter page tells the same 64-bit story.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Pointers or zero values
A pointer decodes absent as nil and empty as a pointer to a blank string — states your code can tell apart, at the price of nil checks. Zero-value mode folds the two together: fine for display, wrong for PATCH-style partial updates.
json.Number keeps 64-bit identifiers exact
Snowflake-style IDs overflow a double past 2^53; past int64's nineteen digits integer decoding fails. The json-number setting swaps numeric fields for json.Number and adds the encoding/json import — raw digits survive until you call Int64 or String.
The initialisms table is the Go wiki's
Thirty-eight acronyms from the CodeReviewComments list — ID, URL, API, HTTP and the rest — are upper-cased wherever one appears as a word inside a key. The toggle reverts user_id to UserId for codebases that lint differently.
Empty and mixed arrays stay honest
An empty array proves nothing about its element type: []any plus a chip asking for a sample with elements. Mixed element types get []any with an inline comment, never a guessed union; the spelling toggles to interface{} for pre-1.18 toolchains.
Keyword keys survive
A key colliding with a Go keyword is renamed — type becomes Type — while its tag keeps the exact key. Keys a raw-string tag cannot hold, like one containing a backtick, draw an honest comment instead of a broken tag.
Unicode keys: exact tag, ASCII field
café becomes the field Cafe, accent folded away, while its json tag keeps café byte-for-byte. A chip lists each rename; past twenty, one summary line says the rest went unlisted.
Nested types are named by their path
A struct reached through a data field is named Data; the same key nested again becomes DataData, not an arbitrary number. Only when even the parent-prefixed name is taken does a numeric suffix appear, flagged by a collision chip.
Common errors
Sample 2: Unexpected "'" at line 1, column 2 — probable cause: single-quoted strings are JSON5, not JSON; use double quotes.- Each tab must hold strict JSON before merging starts. The prefix names the failing tab (shown only with several samples); the caret frame marks the character, and Jump to error focuses it.
Go type generation failed: Syntax error in input JSON Root: Maximum call stack size exceeded- The inference engine recurses and gives up on absurdly deep nesting; everything after the fixed prefix is its thrown reason, top-level type name included. Trim the depth and rerun — input stays put.
Input is larger than the 10 MB limit for this tool.- Past the combined-sample ceiling the run is refused whole, never quietly trimmed. Shape comes from variety, not volume: keep a few representative records, or point the quicktype command below at the full export.
integer 9223372036854775807 is beyond ±2^53 — JavaScript-based tools round it; Go's int64 decodes it exactly- A warning, not a failure — the struct still generates. The scan reads raw tokens before any float conversion, so the digits shown are the ones you pasted.
"tags" is an empty array in every sample — element type unknowable; add a sample with elements- Chips ride under the output without blocking it. Element types come only from evidence: add a payload where tags holds something and []any tightens.
FAQ
Why is an optional field a *string pointer instead of plain string?
Because at least one sample lacked the key, and a pointer is the only shape where absent (nil) and present-but-empty stay different. If nothing downstream cares, choose zero values — the pointer goes, the omitempty tag stays.
Why does the output say UserID rather than UserId?
Go's review convention treats initialisms as units: ID, URL and API keep their case wherever they land in a name, as the linters descended from golint expect. Switching the initialisms option off restores plain camel casing.
When is json.Number worth the extra step?
Whenever a numeric field is really an identifier. Past 2^53 a float64 silently loses precision; past int64's range decoding fails outright. json.Number defers conversion, keeping a digit string until you ask for Int64 or Float64.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Re-run generation now; above 2 MB this is how a run starts.
- ⌘/Ctrl + S
- Download the declarations as a .go file, not the web page.
- ⌘/Ctrl + C
- Copies the whole Go output when nothing is selected.
- ⌘/Ctrl + Shift + C
- Copy everything even while a selection is active.
- t
- Switch theme; inert while a text field has focus.
- ? or ⌘/Ctrl + /
- Show the shortcut overlay.
- Esc
- Close whatever dialog is on top.