Go’s encoding/json package maps JSON values to structs, maps, slices, and primitive types. For APIs, structs provide the clearest contract.
Encode a struct
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email,omitempty"`
}
data, err := json.Marshal(User{ID: 1, Name: "Ada"})
omitempty removes a field when it has its zero value. Avoid using it when clients must distinguish “not supplied” from an explicit zero.
Decode request JSON strictly
func decodeJSON(r *http.Request, dst any) error {
dec := json.NewDecoder(http.MaxBytesReader(nil, r.Body, 1<<20))
dec.DisallowUnknownFields()
if err := dec.Decode(dst); err != nil {
return err
}
if dec.Decode(&struct{}{}) != io.EOF {
return errors.New("body must contain one JSON value")
}
return nil
}
In a real handler, pass the ResponseWriter to MaxBytesReader. Limiting the body prevents an oversized request from consuming unbounded memory. Rejecting unknown fields catches client typos instead of ignoring them.
Validate after decoding
JSON decoding proves that input has the correct representation, not that it is acceptable business data.
type CreateUser struct {
Name string `json:"name"`
Email string `json:"email"`
}
func (in CreateUser) Validate() error {
if strings.TrimSpace(in.Name) == "" {
return errors.New("name is required")
}
if _, err := mail.ParseAddress(in.Email); err != nil {
return errors.New("email is invalid")
}
return nil
}
Write JSON responses consistently
func writeJSON(w http.ResponseWriter, status int, value any) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
return json.NewEncoder(w).Encode(value)
}
Set headers before calling WriteHeader. Do not expose raw internal errors in public JSON responses; map them to stable error codes and log the detailed cause separately.
Numbers and optional fields
Decoding into any converts JSON numbers to float64 by default. Call Decoder.UseNumber when exact numeric representation matters. Use pointer fields or a custom optional type when an update endpoint must distinguish an omitted field from a supplied zero value.