Skip to content

CustomEncoder

The json library provides two custom encoder interfaces, allowing you to register dedicated serialization logic for custom types.

CustomEncoder Interface

Global custom encoder interface that replaces the default encoding behavior.

go
type CustomEncoder interface {
    Encode(value any) (string, error)
}

Configuration: Set via the Config.CustomEncoder field.

go
import stdjson "encoding/json"

type UpperCaseEncoder struct{}

func (e *UpperCaseEncoder) Encode(value any) (string, error) {
    switch v := value.(type) {
    case string:
        return fmt.Sprintf(`"%s"`, strings.ToUpper(v)), nil
    default:
        data, err := stdjson.Marshal(v)
        return string(data), err
    }
}

cfg := json.DefaultConfig()
cfg.CustomEncoder = &UpperCaseEncoder{}
processor, err := json.New(cfg)
if err != nil {
    panic(err)
}

TypeEncoder Interface

Type-specific encoder interface for encoding specific types.

go
type TypeEncoder interface {
    Encode(v reflect.Value) (string, error)
}

Configuration: Register via the Config's CustomTypeEncoders field.

go
type TimeTypeEncoder struct{}

func (e *TimeTypeEncoder) Encode(v reflect.Value) (string, error) {
    if v.Type() == reflect.TypeOf(time.Time{}) {
        t := v.Interface().(time.Time)
        return fmt.Sprintf(`"%s"`, t.Format(time.RFC3339)), nil
    }
    return "", fmt.Errorf("unsupported type: %v", v.Type())
}

// Register type encoder
cfg := json.DefaultConfig()
cfg.CustomTypeEncoders = map[reflect.Type]json.TypeEncoder{
    reflect.TypeOf(time.Time{}): &TimeTypeEncoder{},
}
processor, err := json.New(cfg)
if err != nil {
    panic(err)
}

Custom Encoder Examples

Complete CustomEncoder Implementation

go
import stdjson "encoding/json"

type CompactEncoder struct{}

func (e *CompactEncoder) Encode(value any) (string, error) {
    // Compact encoding using standard library to avoid infinite recursion
    data, err := stdjson.Marshal(value)
    if err != nil {
        return "", err
    }
    return string(data), nil
}

cfg := json.DefaultConfig()
cfg.CustomEncoder = &CompactEncoder{}
processor, err := json.New(cfg)
if err != nil {
    panic(err)
}

TypeEncoder for Type Mapping

go
type CustomTypeEncoder struct{}

func (e *CustomTypeEncoder) Encode(v reflect.Value) (string, error) {
    switch v.Kind() {
    case reflect.Struct:
        return "struct:" + v.Type().Name(), nil
    case reflect.Slice:
        return "slice", nil
    default:
        return v.Kind().String(), nil
    }
}

CustomEncoder vs TypeEncoder Comparison

FeatureCustomEncoderTypeEncoder
ScopeGlobal, replaces default encodingType-specific encoding
Configuration fieldConfig.CustomEncoderConfig.CustomTypeEncoders
Function signatureEncode(any) (string, error)Encode(reflect.Value) (string, error)
Return valuestring (JSON string)string (JSON string)
Use caseUnified encoding behaviorCustom type serialization mapping
FieldTypeDescription
CustomEncoderCustomEncoderCustom encoder interface
CustomTypeEncodersmap[reflect.Type]TypeEncoderPer-type registered encoders

See Also