Тип реализует json.Marshaler (MarshalJSON() ([]byte, error)) и/или json.Unmarshaler (UnmarshalJSON([]byte) error). Тогда json.Marshal/Unmarshal вызывают эти методы. Используют для нестандартного формата (например, число как строка), оберток над time.Time, enum. В UnmarshalJSON нужно разобрать []byte и присвоить полям; при ошибке вернуть json.UnmarshalTypeError или свою ошибку.
func (e *Enum) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil { return err }
switch s {
case "a": *e = EnumA
default: return fmt.Errorf("invalid enum")
}
return nil
}