JSON to Go Struct
Local processing · verifiedTurn a JSON response into Go structs with json tags.
Conversion runs in your browser. Your API responses — often containing tokens — never leave the device. How to verify this yourself.
Why hand-written struct tags drift, and what this generator does differently
Paste a JSON document and this page writes the Go struct definitions that would decode it. Everything runs in the tab — the JSON is read by the browser's own parser and the Go source is assembled as text. There is no upload and no account. The rest of this page is about the four details that decide whether the generated code actually works against the API you copied, because a struct that compiles is not the same as a struct that decodes.
The json tag is the part that matters, and hand-written tags drift
A struct tag tells Go which JSON key a field belongs to. If the tag is wrong the field silently stays at its zero
value: no error, no warning, just an empty string where you expected a customer name. Go's decoder matches a key to
a field in two ways — an exact tag match, then a case-insensitive match against the field name. The trap is that
the comparison ignores letter case but not separators. A field called UserID will happily decode the
key userid or USERID, but it will not decode user_id, because the underscore
is a real character. That is exactly why this generator keeps the original key inside the tag, so
{"user_id": 1} becomes UserID int64 `json:"user_id"` rather than relying on the
field name to match.
The other half of the problem is that tags rot. A field is added on the server, someone renames a key from
created to created_at, and the Go client keeps compiling while quietly decoding nothing.
Regenerating from a real response and diffing the result is a cheap way to catch that before it reaches production.
omitempty is about zero values, not about optionality
The single most common bug in generated Go clients is a field tagged omitempty that needs to be able
to send zero. omitempty only changes encoding: when you marshal the struct, a field holding its zero
value — 0, "", false, nil, or an empty slice or map — is
dropped from the output. So a request body built from Quantity int `json:"quantity,omitempty"` cannot
express "set the quantity to zero": the key disappears, and the server reads it as "leave the quantity alone".
The same tag does nothing at all on the way in. Decoding treats a missing key and a zero-valued key as the same
thing, so omitempty cannot tell you whether the field was absent. If you need that distinction, use a
pointer (*int64) or json.RawMessage, both of which can represent "absent" as
nil. This generator leaves omitempty off by default for precisely this reason, and adds
it everywhere only when you tick the box.
int64, float64 and the 2^53 precision boundary
JSON has one number type, and the parser behind almost every language treats it as a 64-bit float. That means a
64-bit integer ID such as a snowflake — 1276898186410434561 — is already imprecise by the time any
decoder sees it; the value becomes 1276898186410434600. The generator cannot recover the digits that
were lost before it ran, so it does the honest thing instead: whole numbers that fit inside Go's safe integer range
become int64, and anything larger becomes float64 rather than pretending an
int64 can hold it. For real IDs the robust fix is to keep them as JSON strings and parse them on the
server, which is why generated clients for large APIs usually declare ID string `json:"id"`.
Why inconsistent arrays are merged instead of sampled
Many generators look only at the first element of an array, which is how a codebase ends up with a struct that is
missing a field that appears in every later element. This one walks every element and merges them. When the same
key holds different types in different elements, the types are widened: int64 plus
float64 becomes float64, and any other clash — a string in one element and an object in
another, or a value that is sometimes null — becomes interface{}. A field that
is null in a single sample also becomes interface{}, because null carries no
type information. interface{} is not pretty, but it is far better than a struct that
silently drops data and panics later on a nil dereference. Press Sample to see an array whose second element is
missing a field, and watch the merged struct keep it.
How to verify nothing leaves the device
Open the browser developer tools, switch to the Network panel and clear it. Convert a document here and watch: no request carries your JSON. Turn off your network connection and convert again — the tool is unaffected, because the parser, the type inference and the Go source generation are all in the page you already loaded. The only third-party script on the site is the advertising tag, which is configured not to receive anything you type. The privacy policy lists every script and what it can see.
Common questions
- Does this upload my API response anywhere?
No. The JSON is parsed with the browser's own JSON.parse and the struct text is built by the page. You can prove it by opening the browser network panel and converting a document — no request carries your data — or by going offline and converting again, which still works. This matters because API responses routinely carry bearer tokens and customer records.
- Why does my field get int64 when my API sometimes returns a decimal?
The generator merges every array element and widens conflicting number types. If one element has 10 and another has 10.5, the merged type becomes float64. If you know the field is always whole, set Numbers to int64, but be aware that Go's int64 cannot represent everything JSON can — see the precision note in the tutorial.
- What does omitempty actually do to my struct?
It only affects encoding: when you marshal the struct back to JSON, a field holding its zero value (0, "", false, nil, an empty slice or map) is left out of the output. It has no effect on decoding. That means a field that must be able to send an explicit 0 or "" should never be tagged omitempty, because "zero" and "absent" become indistinguishable.
- How are nested objects and a top-level array named?
Each nested object becomes its own struct named after its parent plus the field name, so a profile object inside the root struct becomes Profile AutoGeneratedProfile with a type of its own. If the top level of your JSON is an array, the element shape is used as the template and the file opens with type AutoGenerated []AutoGeneratedItem, which you can rename to whatever your handler expects.