patterngoModerate
Custom JSON marshaling with MarshalJSON and UnmarshalJSON
Viewed 0 times
custom JSON marshalingMarshalJSONUnmarshalJSONjson.Marshalerjson.Unmarshalerenum JSONtype alias infinite recursion
Problem
Built-in JSON encoding doesn't handle custom types (enums, encrypted fields, time formats other than RFC3339, union types) without explicit control.
Solution
Implement json.Marshaler and json.Unmarshaler interfaces:
type Status int
const (
StatusActive Status = iota
StatusInactive
)
func (s Status) MarshalJSON() ([]byte, error) {
names := map[Status]string{StatusActive: "active", StatusInactive: "inactive"}
name, ok := names[s]
if !ok {
return nil, fmt.Errorf("unknown status: %d", s)
}
return json.Marshal(name)
}
func (s *Status) UnmarshalJSON(data []byte) error {
var name string
if err := json.Unmarshal(data, &name); err != nil {
return err
}
codes := map[string]Status{"active": StatusActive, "inactive": StatusInactive}
v, ok := codes[name]
if !ok {
return fmt.Errorf("unknown status: %q", name)
}
*s = v
return nil
}Why
encoding/json checks for MarshalJSON / UnmarshalJSON methods before using reflection. This allows full control over the wire format for any type.
Gotchas
- MarshalJSON must be on the value receiver if the type is used by value; UnmarshalJSON must be on a pointer receiver to modify the value
- Calling json.Marshal inside MarshalJSON on the same type causes infinite recursion — use a type alias to break the cycle
- The json.RawMessage type lets you defer or embed raw JSON without double-encoding
Revisions (0)
No revisions yet.