Summary
Go types can expose custom wire formats through encoding/json and related packages. The main choices are MarshalText / UnmarshalText, MarshalJSON / UnmarshalJSON, or explicit helper functions. Each option trades off convenience, control, and complexity.
1. MarshalText / UnmarshalText
Implements encoding.TextMarshaler and encoding.TextUnmarshaler.
This converts values to plain text. JSON, XML, and other encoders automatically use it when a custom JSON marshaler is absent.
func (n Name) MarshalText() ([]byte, error) {
return []byte(n.value), nil
}
func (n *Name) UnmarshalText(data []byte) error {
n.value = string(data)
return nil
}
2. MarshalJSON / UnmarshalJSON
Implements json.Marshaler and json.Unmarshaler.
This gives full control over the JSON representation.
func (n Name) MarshalJSON() ([]byte, error) {
return json.Marshal("prefix-" + n.value)
}
func (n *Name) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
if !strings.HasPrefix(s, "prefix-") {
return errors.New("invalid format")
}
n.value = strings.TrimPrefix(s, "prefix-")
return nil
}
3. Helper function (MarshalName)
No interface implementation. Useful for tests or custom glue code without exposing marshaler interfaces.
func MarshalName(n Name) ([]byte, error) {
return []byte(n.value), nil
}
Comparison
| Method | Interface | encoding/json ready? |
Control over JSON | Complexity |
|---|---|---|---|---|
MarshalText |
encoding.TextMarshaler |
Yes | Partial | Low |
MarshalJSON |
json.Marshaler |
Yes | Full | High |
| Helper function | None | No | Manual | Low |
Choosing between them
MarshalText: ideal for strong types with a simple text representation.MarshalJSON: choose it when the JSON structure itself must be customised.- Helper function: useful for isolated scenarios or tests without changing the type.
Key detail
encoding/json uses MarshalText automatically if MarshalJSON is not present, so in many cases implementing MarshalText is enough.