blob: 503910716741855a6a7bbd1fc8217c6e88aad0cf [file] [log] [blame]
package util
import (
"encoding/json"
"reflect"
)
// Codec serializes/deserializes an instance of a type to/from byte arrays.
// Encode and Decode have to be the inverse of each other.
type Codec interface {
// Encode serializes the given value to a byte array (inverse of Decode).
Encode(interface{}) ([]byte, error)
// Decode deserializes the byte array to an instance of the type that
// was passed to Encode in a prior call.
Decode([]byte) (interface{}, error)
}
// JSONCodec implements the Codec interface by serializing and
// deserializing instances of the underlying type of 'instance'.
// Generally it's assumed that 'instance' is a struct, a pointer to
// a struct, a slice or a map.
type JSONCodec struct {
targetType reflect.Type // the type we want to encode/decode.
stripPtr bool // indicates whether to dereference the pointer of the result.
}
// NewJSONCodec returns a new JSONCodec instance.
func NewJSONCodec(instance interface{}) *JSONCodec {
targetType := reflect.TypeOf(instance)
if targetType.Kind() == reflect.Ptr {
targetType = targetType.Elem()
}
kind := targetType.Kind()
return &JSONCodec{
targetType: targetType,
stripPtr: (kind == reflect.Slice) || (kind == reflect.Map),
}
}
// See Codec interface.
func (j *JSONCodec) Encode(data interface{}) ([]byte, error) {
return json.Marshal(data)
}
// See Codec interface.
func (j *JSONCodec) Decode(byteData []byte) (interface{}, error) {
// Get a pointer to a new instance, because that's what Unmarshal needs.
ret := reflect.New(j.targetType).Interface()
err := json.Unmarshal(byteData, ret)
if err != nil {
return nil, err
} else if j.stripPtr {
// Strip the pointer for slices and maps.
return reflect.ValueOf(ret).Elem().Interface(), nil
}
return ret, nil
}