Go is a statically typed language, which means you need explicit struct definitions to work with JSON data. Unlike JavaScript or Python, you can't just parse JSON into a dynamic map and access properties freely. Understanding how to map JSON to Go structs is essential for building robust Go applications.
Convert JSON to Go structs instantly with our free tool â
Why Go Needs Struct Definitions
Go's encoding/json package uses struct tags and reflection to marshal and unmarshal JSON. Without a proper struct definition, you're limited to map[string]interface{}, which loses type safety and requires runtime type assertions.
- Type safety â the compiler catches type mismatches at build time
- Performance â struct access is significantly faster than map lookups
- Autocompletion â your editor knows the available fields and their types
- Documentation â struct definitions serve as self-documenting API contracts
Type Mappings
JSON types map to Go types as follows:
| JSON Type | Go Type | Notes |
|---|---|---|
| string | string | UTF-8 encoded |
| number (int) | int / int64 | Use int64 for large values |
| number (float) | float64 | Default for JSON numbers |
| boolean | bool | |
| null | *T (pointer) | nil when null |
| array | []T | Slice of typed elements |
| object | struct | Named struct type |
JSON Struct Tags
Go uses struct field tags to control how JSON keys map to struct fields. The json tag is essential:
// JSON
{
"user_name": "alice",
"email_address": "alice@example.com",
"is_active": true,
"login_count": 42
}
// Go struct
type User struct {
UserName string `json:"user_name"`
EmailAddress string `json:"email_address"`
IsActive bool `json:"is_active"`
LoginCount int `json:"login_count"`
}Field names in Go must be exported (capitalized) to be visible to the JSON package. The struct tag maps the exported Go name to the lowercase JSON key.
The omitempty Option
The omitempty tag option tells the JSON encoder to skip fields with zero values when marshaling. This is critical for building flexible API payloads:
type UpdateRequest struct {
Name string `json:"name,omitempty"`
Email string `json:"email,omitempty"`
Age *int `json:"age,omitempty"` // pointer: distinguish 0 from absent
Bio *string `json:"bio,omitempty"` // pointer: distinguish "" from absent
}
// Only "name" appears in the output:
req := UpdateRequest{Name: "Alice"}
data, _ := json.Marshal(req)
// {"name":"Alice"}Zero values by type: "" for strings, 0 for numbers, false for bools, nil for pointers/slices/maps. Use pointers (*int, *string) when you need to distinguish between "not set" and "zero value".
Nested Structs
Complex JSON with nested objects requires separate struct types for each level:
// JSON
{
"id": 1,
"name": "Alice",
"company": {
"name": "Acme Corp",
"address": {
"city": "Springfield",
"country": "US"
}
},
"skills": ["go", "rust", "python"]
}
// Go structs
type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
type Company struct {
Name string `json:"name"`
Address Address `json:"address"`
}
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Company Company `json:"company"`
Skills []string `json:"skills"`
}Tip: For deeply nested JSON, define each struct separately. Avoid anonymous structs for reusable sub-objects. Use inline anonymous structs only for one-off, non-reusable shapes.
Common Patterns
Here are patterns you'll frequently encounter when working with JSON in Go:
// Pattern 1: API response wrapper
type APIResponse[T any] struct {
Data T `json:"data"`
Message string `json:"message"`
Status int `json:"status"`
}
// Pattern 2: Flexible field with json.RawMessage
type Event struct {
Type string `json:"type"`
Payload json.RawMessage `json:"payload"` // decode later based on Type
}
// Pattern 3: Custom JSON key with "-" to ignore
type Internal struct {
PublicID string `json:"id"`
SecretKey string `json:"-"` // never marshaled/unmarshaled
}
// Pattern 4: Unmarshal usage
var user User
err := json.Unmarshal([]byte(jsonString), &user)
if err != nil {
log.Fatal(err)
}
fmt.Println(user.Name) // "Alice"Automate the Conversion
Writing Go structs by hand from large JSON payloads is tedious. Our JSON to Go converter automatically generates properly tagged struct definitions from any JSON input â including nested objects, arrays, and optional fields.