Custom Go Type Aliases for JSON Fields
Create custom type aliases and named types in Go for stronger typing of JSON fields. Covers enums, custom marshalers, and domain-specific types.
Detailed Explanation
Custom Types for Stronger JSON Typing
Go lets you define named types based on primitives, giving you type safety, custom methods, and domain semantics that raw strings and ints cannot provide.
Enum-Like Types
{
"id": 1,
"status": "active",
"role": "admin",
"priority": 3
}
type Status string
const (
StatusActive Status = "active"
StatusInactive Status = "inactive"
StatusPending Status = "pending"
)
type Role string
const (
RoleAdmin Role = "admin"
RoleEditor Role = "editor"
RoleViewer Role = "viewer"
)
type Priority int
const (
PriorityLow Priority = 1
PriorityMedium Priority = 2
PriorityHigh Priority = 3
)
type Ticket struct {
ID int `json:"id"`
Status Status `json:"status"`
Role Role `json:"role"`
Priority Priority `json:"priority"`
}
Why Custom Types Matter
Named types prevent mixing up values that are technically the same Go type:
func AssignTicket(role Role, status Status) { ... }
// Cannot accidentally pass a Status where Role is expected
Custom Marshaling
For types that need special serialization, implement json.Marshaler and json.Unmarshaler:
type Cents int
func (c Cents) MarshalJSON() ([]byte, error) {
dollars := float64(c) / 100.0
return json.Marshal(dollars)
}
func (c *Cents) UnmarshalJSON(b []byte) error {
var dollars float64
if err := json.Unmarshal(b, &dollars); err != nil {
return err
}
*c = Cents(dollars * 100)
return nil
}
This type stores money as integer cents internally but serializes as decimal dollars in JSON.
Validation on Unmarshal
Custom types can validate values during unmarshaling:
func (s *Status) UnmarshalJSON(b []byte) error {
var str string
json.Unmarshal(b, &str)
switch Status(str) {
case StatusActive, StatusInactive, StatusPending:
*s = Status(str)
return nil
}
return fmt.Errorf("invalid status: %s", str)
}
This rejects unknown status values at parse time instead of letting invalid data propagate through your system.
Use Case
Domain-driven design in Go benefits from custom types for enums, money values, and identifiers. They catch bugs at compile time and make the code self-documenting when converting JSON API data.