Skip to content

Commit

Permalink
Allow custom unmarshal implementation (#185)
Browse files Browse the repository at this point in the history
  • Loading branch information
errorhandler authored Feb 8, 2022
1 parent 48a755c commit 0964a5e
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 0 deletions.
12 changes: 12 additions & 0 deletions ion/unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ var typesAcceptableKinds = map[Type][]reflect.Kind{
ListType: {reflect.Slice, reflect.Array},
}

// Unmarshaler is the interface implemented by types that can unmarshal themselves to Ion.
type Unmarshaler interface {
UnmarshalIon(r Reader) error
}

// Unmarshal unmarshals Ion data to the given object.
//
// User must pass the proper object type to the unmarshalled Ion data.
Expand Down Expand Up @@ -289,6 +294,8 @@ func (d *Decoder) DecodeTo(v interface{}) error {
return d.decodeTo(rv)
}

var unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()

func (d *Decoder) decodeTo(v reflect.Value) error {
if !v.IsValid() {
// Don't actually have anywhere to put this value; skip it.
Expand All @@ -305,6 +312,11 @@ func (d *Decoder) decodeTo(v reflect.Value) error {
return nil
}

t := v.Type()
if t.Kind() != reflect.Ptr && v.CanAddr() && reflect.PtrTo(t).Implements(unmarshalerType) {
return v.Addr().Interface().(Unmarshaler).UnmarshalIon(d.r)
}

switch d.r.Type() {
case BoolType:
return d.decodeBoolTo(v)
Expand Down
23 changes: 23 additions & 0 deletions ion/unmarshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,29 @@ func TestUnmarshalStructBinary(t *testing.T) {
test(ionByteValue, "structToStruct", &foo{}, &foo{2}) // unmarshal IonStruct to a Go struct
}

type unmarshalMe struct {
Name string
custom bool
}

func (u *unmarshalMe) UnmarshalIon(r Reader) error {
u.custom = true
return nil
}

func TestUnmarshalCustomMarshaler(t *testing.T) {
ionBinary, err := MarshalBinary(unmarshalMe{
Name: "John Doe",
})
require.NoError(t, err)

dec := NewDecoder(NewReader(bytes.NewReader(ionBinary)))
var decodedResult unmarshalMe

assert.NoError(t, dec.DecodeTo(&decodedResult))
assert.True(t, decodedResult.custom)
}

func TestUnmarshalListSexpBinary(t *testing.T) {
test := func(data []byte, testName string, val, eval interface{}) {
t.Run("reflect.TypeOf(val).String()", func(t *testing.T) {
Expand Down

0 comments on commit 0964a5e

Please sign in to comment.