Tools
Tools: JSON to Go Struct: The Complete Conversion Guide for 2026
2026-02-27
0 views
admin
Basic Struct Generation ## JSON Tags Explained ## Nested Structs and Arrays ## Nullable and Optional Fields ## Decoding JSON ## Encoding JSON ## Dynamic JSON with map[string]interface{} ## Custom Marshaling ## Tools: json-to-go and quicktype ## Common Pitfalls Converting JSON to Go structs is a daily task for Go backend developers. Here's everything you need to know. For automatic struct generation from JSON: DevToolBox JSON to Go converter — paste JSON, get Go struct instantly Convert JSON to Go structs instantly with DevToolBox's free JSON to Go tool — handles nested objects, arrays, nullable fields, and Go naming conventions. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK:
{ "id": 1, "name": "Alice", "email": "[email protected]", "active": true
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{ "id": 1, "name": "Alice", "email": "[email protected]", "active": true
} CODE_BLOCK:
{ "id": 1, "name": "Alice", "email": "[email protected]", "active": true
} CODE_BLOCK:
type User struct { ID int `json:"id"` Name string `json:"name"` Email string `json:"email"` Active bool `json:"active"`
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
type User struct { ID int `json:"id"` Name string `json:"name"` Email string `json:"email"` Active bool `json:"active"`
} CODE_BLOCK:
type User struct { ID int `json:"id"` Name string `json:"name"` Email string `json:"email"` Active bool `json:"active"`
} CODE_BLOCK:
type Product struct { // "json" tag maps field to JSON key ProductID int `json:"product_id"` // omitempty: skip field if zero value Description string `json:"description,omitempty"` // "-": always skip this field Internal string `json:"-"` // string: marshal number as JSON string Price float64 `json:"price,string"`
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
type Product struct { // "json" tag maps field to JSON key ProductID int `json:"product_id"` // omitempty: skip field if zero value Description string `json:"description,omitempty"` // "-": always skip this field Internal string `json:"-"` // string: marshal number as JSON string Price float64 `json:"price,string"`
} CODE_BLOCK:
type Product struct { // "json" tag maps field to JSON key ProductID int `json:"product_id"` // omitempty: skip field if zero value Description string `json:"description,omitempty"` // "-": always skip this field Internal string `json:"-"` // string: marshal number as JSON string Price float64 `json:"price,string"`
} CODE_BLOCK:
type Order struct { OrderID string `json:"order_id"` Customer Customer `json:"customer"` // nested struct Items []Item `json:"items"` // array of structs Tags []string `json:"tags"` // array of primitives Meta map[string]interface{} `json:"meta"` // dynamic object
} type Customer struct { Name string `json:"name"` Email string `json:"email"` Address Address `json:"address"`
} type Address struct { Street string `json:"street"` City string `json:"city"` Zip string `json:"zip"`
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
type Order struct { OrderID string `json:"order_id"` Customer Customer `json:"customer"` // nested struct Items []Item `json:"items"` // array of structs Tags []string `json:"tags"` // array of primitives Meta map[string]interface{} `json:"meta"` // dynamic object
} type Customer struct { Name string `json:"name"` Email string `json:"email"` Address Address `json:"address"`
} type Address struct { Street string `json:"street"` City string `json:"city"` Zip string `json:"zip"`
} CODE_BLOCK:
type Order struct { OrderID string `json:"order_id"` Customer Customer `json:"customer"` // nested struct Items []Item `json:"items"` // array of structs Tags []string `json:"tags"` // array of primitives Meta map[string]interface{} `json:"meta"` // dynamic object
} type Customer struct { Name string `json:"name"` Email string `json:"email"` Address Address `json:"address"`
} type Address struct { Street string `json:"street"` City string `json:"city"` Zip string `json:"zip"`
} CODE_BLOCK:
// Use pointers for nullable/optional fields
type User struct { ID int `json:"id"` Name string `json:"name"` Bio *string `json:"bio"` // null or string AvatarURL *string `json:"avatar_url,omitempty"` // may be absent
} // Decoding
var user User
json.Unmarshal(data, &user) if user.Bio != nil { fmt.Println("Bio:", *user.Bio)
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
// Use pointers for nullable/optional fields
type User struct { ID int `json:"id"` Name string `json:"name"` Bio *string `json:"bio"` // null or string AvatarURL *string `json:"avatar_url,omitempty"` // may be absent
} // Decoding
var user User
json.Unmarshal(data, &user) if user.Bio != nil { fmt.Println("Bio:", *user.Bio)
} CODE_BLOCK:
// Use pointers for nullable/optional fields
type User struct { ID int `json:"id"` Name string `json:"name"` Bio *string `json:"bio"` // null or string AvatarURL *string `json:"avatar_url,omitempty"` // may be absent
} // Decoding
var user User
json.Unmarshal(data, &user) if user.Bio != nil { fmt.Println("Bio:", *user.Bio)
} CODE_BLOCK:
import ( "encoding/json" "net/http"
) // From bytes
var user User
if err := json.Unmarshal(data, &user); err != nil { return fmt.Errorf("parse user: %w", err)
} // From HTTP response
resp, err := http.Get("https://api.example.com/users/1")
if err != nil { /* handle */ }
defer resp.Body.Close() var user User
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil { return fmt.Errorf("decode user: %w", err)
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
import ( "encoding/json" "net/http"
) // From bytes
var user User
if err := json.Unmarshal(data, &user); err != nil { return fmt.Errorf("parse user: %w", err)
} // From HTTP response
resp, err := http.Get("https://api.example.com/users/1")
if err != nil { /* handle */ }
defer resp.Body.Close() var user User
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil { return fmt.Errorf("decode user: %w", err)
} CODE_BLOCK:
import ( "encoding/json" "net/http"
) // From bytes
var user User
if err := json.Unmarshal(data, &user); err != nil { return fmt.Errorf("parse user: %w", err)
} // From HTTP response
resp, err := http.Get("https://api.example.com/users/1")
if err != nil { /* handle */ }
defer resp.Body.Close() var user User
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil { return fmt.Errorf("decode user: %w", err)
} CODE_BLOCK:
user := User{ID: 1, Name: "Alice", Email: "[email protected]"} // To bytes
data, err := json.Marshal(user) // Pretty print
data, err := json.MarshalIndent(user, "", " ") // To writer (e.g., http.ResponseWriter)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user) Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
user := User{ID: 1, Name: "Alice", Email: "[email protected]"} // To bytes
data, err := json.Marshal(user) // Pretty print
data, err := json.MarshalIndent(user, "", " ") // To writer (e.g., http.ResponseWriter)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user) CODE_BLOCK:
user := User{ID: 1, Name: "Alice", Email: "[email protected]"} // To bytes
data, err := json.Marshal(user) // Pretty print
data, err := json.MarshalIndent(user, "", " ") // To writer (e.g., http.ResponseWriter)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user) CODE_BLOCK:
// When structure is unknown
var result map[string]interface{}
json.Unmarshal(data, &result) // Type assertions needed for nested access
if name, ok := result["name"].(string); ok { fmt.Println(name)
} // Better: json.RawMessage for delayed decoding
type Response struct { Status string `json:"status"` Data json.RawMessage `json:"data"` // decode later based on status
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
// When structure is unknown
var result map[string]interface{}
json.Unmarshal(data, &result) // Type assertions needed for nested access
if name, ok := result["name"].(string); ok { fmt.Println(name)
} // Better: json.RawMessage for delayed decoding
type Response struct { Status string `json:"status"` Data json.RawMessage `json:"data"` // decode later based on status
} CODE_BLOCK:
// When structure is unknown
var result map[string]interface{}
json.Unmarshal(data, &result) // Type assertions needed for nested access
if name, ok := result["name"].(string); ok { fmt.Println(name)
} // Better: json.RawMessage for delayed decoding
type Response struct { Status string `json:"status"` Data json.RawMessage `json:"data"` // decode later based on status
} CODE_BLOCK:
// Custom time format
type Event struct { Name string `json:"name"` CreatedAt time.Time `json:"created_at"`
} // time.Time marshals to RFC3339 by default: "2026-02-27T10:00:00Z" // Custom format: implement json.Marshaler
type CustomDate struct { time.Time
} func (d CustomDate) MarshalJSON() ([]byte, error) { return json.Marshal(d.Format("2006-01-02"))
} func (d *CustomDate) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } t, err := time.Parse("2006-01-02", s) d.Time = t return err
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
// Custom time format
type Event struct { Name string `json:"name"` CreatedAt time.Time `json:"created_at"`
} // time.Time marshals to RFC3339 by default: "2026-02-27T10:00:00Z" // Custom format: implement json.Marshaler
type CustomDate struct { time.Time
} func (d CustomDate) MarshalJSON() ([]byte, error) { return json.Marshal(d.Format("2006-01-02"))
} func (d *CustomDate) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } t, err := time.Parse("2006-01-02", s) d.Time = t return err
} CODE_BLOCK:
// Custom time format
type Event struct { Name string `json:"name"` CreatedAt time.Time `json:"created_at"`
} // time.Time marshals to RFC3339 by default: "2026-02-27T10:00:00Z" // Custom format: implement json.Marshaler
type CustomDate struct { time.Time
} func (d CustomDate) MarshalJSON() ([]byte, error) { return json.Marshal(d.Format("2006-01-02"))
} func (d *CustomDate) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } t, err := time.Parse("2006-01-02", s) d.Time = t return err
} COMMAND_BLOCK:
npm install -g quicktype
quicktype --lang go --out types.go data.json Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
npm install -g quicktype
quicktype --lang go --out types.go data.json COMMAND_BLOCK:
npm install -g quicktype
quicktype --lang go --out types.go data.json CODE_BLOCK:
// To always marshal as [] not null:
type User struct { Tags []string `json:"tags"`
} user := User{} // Tags is nil
data, _ := json.Marshal(user) // {"tags": null} user.Tags = make([]string, 0) // empty slice
data, _ = json.Marshal(user) // {"tags": []} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
// To always marshal as [] not null:
type User struct { Tags []string `json:"tags"`
} user := User{} // Tags is nil
data, _ := json.Marshal(user) // {"tags": null} user.Tags = make([]string, 0) // empty slice
data, _ = json.Marshal(user) // {"tags": []} CODE_BLOCK:
// To always marshal as [] not null:
type User struct { Tags []string `json:"tags"`
} user := User{} // Tags is nil
data, _ := json.Marshal(user) // {"tags": null} user.Tags = make([]string, 0) // empty slice
data, _ = json.Marshal(user) // {"tags": []} - DevToolBox JSON to Go converter — paste JSON, get Go struct instantly
- quicktype CLI: - json-to-go (classic web tool): josefpihrt/json-to-go - Unexported fields are ignored — start struct field names with uppercase
- Integers vs floats — JSON numbers decode as float64 in interface{}
- Missing omitempty — zero values (0, "", false) get marshaled unless omitempty
- Nil vs empty slice — var s []int marshals as null, s := []int{} marshals as []
how-totutorialguidedev.toai