JSON to Go Struct

Paste JSON and get Go structs with json tags. Nested objects become separate types, array elements get merged, and integers stay int instead of float64.

Need TypeScript instead? JSON to TypeScript

About JSON to Go Struct

Paste JSON and get Go structs that compile. Field names follow Go conventions (order_id → OrderID, created_at → CreatedAt), and abbreviations like id, url, api, json are upper-cased the way the Go community writes them — OrderID, not OrderId, which is also what staticcheck asks for.

Types are inferred from the sample values, so more samples means better guesses.

Go has no union types: a field gets exactly one type. The rules here are: all samples integers → int; a decimal anywhere → float64; a mix of integers and decimals → float64 rather than collapsing to interface{}; strings and numbers mixed → interface{}, which is not laziness but the limit of what Go can express.

That means a single sample is easy to guess wrong from: a field that happens to be 1 in your sample may be 1.5 in production, and you get int. Paste a few real records before generating.

Three traps worth knowing:

Integers above 2^53 are already wrong before they arrive. In {"id": 1234567890123456789} the number is 1234567890123456789 → 1234567890123456800 after JSON.parse — not this tool's arithmetic, just the 53-bit precision of a JavaScript number. Send snowflake IDs and order numbers as strings, or accept them as json.Number on the server.

null can only become interface{}. In Go the idiomatic way to say "maybe absent" is a pointer (string) or a concrete nullable type such as sql.NullString or time.Time. The tool cannot know your intent, so it picks the widest type. Likewise, a key present in only some records arrives as the zero value, indistinguishable from a genuine zero — make those fields pointers yourself if it matters.

Time strings are not turned into time.Time. Detecting them means adding an import, and JSON timestamps are not always RFC3339 (2026-10-05 14:23:11 is common), so a wrong guess simply fails to compile. Times come out as string.

Colliding field names get a suffix: user_name and userName both become UserName, which Go rejects, so the second one becomes UserName2 with a notice. That is not the tool inventing names — it is telling you two keys in the JSON mean the same thing.

omitempty is off by default. It only affects whether zero values are omitted when marshalling; it does not change what a missing key means.

Everything runs in the browser; the JSON is never uploaded.

Frequently asked questions

Will the generated code compile?

Yes. Each type comes with its package clause, and field names avoid Go keywords and leading digits. Run gofmt after pasting to normalise alignment.

Why is a field interface{}?

It held more than one type across the samples (a string in one record, a number in another), or every value was null. Go has no union types, so the widest type is the only option.

Why did the last digits of a long number change?

Integers above 2^53 lose precision in JSON.parse itself — any JavaScript tool does the same. Pass such IDs as strings.

Why is a timestamp a string?

Detecting it means adding an import, and JSON timestamp formats vary, so a wrong guess fails to compile. Convert it to time.Time yourself.

Can I get Id instead of ID?

Yes, rename it after generating. The tool upper-cases abbreviations like id, url and api following Go community convention.

Two keys became the same field name?

They differ in JSON but collide once converted (user_name and userName). The second gets a numeric suffix; consider unifying the names in the JSON itself.

Is my JSON uploaded?

No, parsing and generation happen in the browser.

Related tools

Encoding View all ›