Guide
How to convert JSON to a Go struct
A practical walkthrough for turning a real JSON payload into Go structs that are ready for encoding/json.
Need it now? JSON → Go struct converter.
Step 1 — Start with a representative JSON sample
A single sample can only infer what it can see. Use a payload that includes nested objects, arrays, null values, and real field names from your API:
{
"id": 42,
"name": "Ada Lovelace",
"email": "ada@example.com",
"is_admin": false,
"profile": {
"company": "Analytical Engine Labs",
"timezone": "Europe/London"
},
"tags": ["math", "engine"],
"last_login_at": null
}Step 2 — Generate the Go struct
Paste the sample into the JSON → Go struct converter. The first pass gives you the shape and json tags:
type Root struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
IsAdmin bool `json:"is_admin"`
Profile Profile `json:"profile"`
Tags []string `json:"tags"`
LastLoginAt any `json:"last_login_at"`
}
type Profile struct {
Company string `json:"company"`
Timezone string `json:"timezone"`
}Step 3 — Polish names, IDs, and optional fields
Generated structs are a starting point. For production code, rename the root type, use int64 for large IDs, and make truly optional fields pointers:
package model
import "time"
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
Email *string `json:"email,omitempty"`
IsAdmin bool `json:"is_admin"`
Profile Profile `json:"profile"`
Tags []string `json:"tags"`
LastLoginAt *time.Time `json:"last_login_at,omitempty"`
}
type Profile struct {
Company string `json:"company"`
Timezone string `json:"timezone"`
}Step 4 — Decode JSON at the API boundary
Once the struct is polished, use the standard library to decode request bodies, fixtures, webhook payloads, or API responses:
func DecodeUser(r io.Reader) (*model.User, error) {
var user model.User
if err := json.NewDecoder(r).Decode(&user); err != nil {
return nil, err
}
return &user, nil
}Step 5 — Review json tags before shipping
Keep tags aligned with the wire format. Add omitempty only when your API should omit zero values during encoding:
b, err := json.Marshal(user)
if err != nil {
return err
}
fmt.Println(string(b))Pointers or values?
- • Use values for required fields where the zero value is meaningful.
- • Use pointers when you need to know whether a field was missing or null.
- • Use custom nullable types when database scanning and JSON encoding both matter.
Common pitfalls
- • Null is not the same as zero. A missing boolean and
falseboth become false unless you use a pointer. - • Empty arrays hide element types. Include at least one representative item when you want useful slice types.
- • Dates are strings in JSON. Switch timestamp fields to
time.Timeonly if your decoder can parse that format. - • One payload is not a contract. Compare multiple responses or API docs before finalizing optional fields.
FAQ
Usually yes. Pointers distinguish missing or null values from normal zero values like false or 0.
Use pointer fields for nullable scalars, or custom nullable types when database scanning and JSON encoding share the same model.
Yes. Use the SQL to Go struct guide when the source of truth is a CREATE TABLE statement.