JSON to Go

Generate Go structs with JSON tags from a JSON sample — nested shapes merged and deduplicated, keys missing from some elements marked omitempty, and every slot that had to become interface{} reported with its reason. Free, no sign-up — runs locally in your browser.

Your work is auto-saved in this browser only, so a refresh won't lose it. Nothing is uploaded.

Why generate Go structs from a sample?

Go is unforgiving about JSON in a way scripting languages are not: encoding/json wants a struct whose fields and tags line up with the payload, and writing that struct by hand from a real API response — three levels of nesting, an array of objects with a field that only sometimes appears — is slow, typo-prone work. The sample already contains every fact the struct needs. This converter extracts them mechanically: paste the response, get back compilable struct definitions with the JSON tags filled in, and spend your own time on the modelling decisions only you can make.

How to use this tool

  1. Paste the JSON. A real API response is ideal; the more representative the sample — edge cases, empty arrays, optional blocks — the more truthful the structs.
  2. Name the root struct. Root is fine for a quick look; for code you will keep, use the name it will wear in your project (OrderResponse, UserProfile).
  3. Press Generate Go. The root struct comes first, the structs it references after it.
  4. Read the notes if any appear. They list every slot that had to become interface — a null, a mixture of types, an empty array — and say which, so nothing was decided silently.
  5. Copy or download the result, then tighten it: swap in time.Time, json.RawMessage or domain types where your knowledge beats the sample's evidence.

The rules it follows

Naming. Structs are PascalCase after the key that holds them; fields are PascalCase as Go requires, with the original key preserved exactly in the json tag. Merging. Array elements are treated as sightings of one struct: keys are unioned, and a key absent from any element is marked ,omitempty instead of spawning a second struct. Numbers. All-whole numbers become int64; any fraction widens the slot to float64. Honesty. Nulls, mixed slots and empty arrays become interface — and each one is reported in the notes above the result, never quietly. A string that looks like a date stays string: JSON has six types, and the generator reports the shape the data has, not the meaning you might project onto it.

Worked example

Given this sample (the second user has no nickname):

{
  "team": "Platform",
  "users": [
    { "id": 1, "name": "Ada", "nickname": "ada" },
    { "id": 2, "name": "Grace" }
  ]
}

the converter emits:

type Root struct {
	Team  string `json:"team"`
	Users []User `json:"users"`
}

type User struct {
	Id       int64  `json:"id"`
	Name     string `json:"name"`
	Nickname string `json:"nickname,omitempty"`
}

The omitempty is the second user talking: the field demonstrably comes and goes, so the tag says so.

Frequently asked questions

How are nested objects and arrays handled?

Each nested object becomes its own named struct, named in PascalCase after the key that holds it (an array under "users" yields struct User). Identical shapes — same keys, same types — are deduplicated into a single shared struct instead of being emitted twice; two different shapes that want the same name get a numeric suffix (Address, Address2). Arrays of objects are merged: the struct describes every element, and a key missing from some elements gets ,omitempty in its JSON tag.

Why is a field typed interface{}?

Because the sample gave the generator nothing more precise to say, and the notes above the result always tell you which case it was: the value was null (Go has no null type), the slot mixed types across the sample (a string here, a number there), or the array was empty so its element type is unknowable. interface{} is the honest answer in all three — the alternative is a confident type your real data would break.

How do you decide between int64 and float64?

From the evidence: if every number seen in a slot is a whole number, the field is int64; the moment any value has a fraction, the slot widens to float64. One caution follows from that: a JSON value of 1.0 is the same number as 1 to any JSON parser, so a field that is always sent as 2.0, 3.0 will read as int64 here. If your API means it as a float, adjust that one field after generating.

Why do the JSON tags matter?

Go's encoding/json matches keys to exported fields case-insensitively by default, but an exact key like "first_name" will not match a field named FirstName on its own merits in every case — the tag pins the mapping down so the struct unmarshals the very JSON you generated it from, underscores and all. Fields keep the original key in the tag even though the Go field name is PascalCase, as Go style requires.

My JSON is invalid and it just shows an error. What now?

The converter reports the line and column of the first syntax error, using the same strict scanner as the JSON tools on this site. Fix that one spot — trailing commas and missing quotes cause most failures — and convert again. This converter deliberately accepts only real JSON, so the structs it emits are never based on a guess about what you meant.

Related tools