Skip to content

Commit

Permalink
Fix error when decoding to not-nil interface
Browse files Browse the repository at this point in the history
The following code used to return error "cbor: cannot unmarshal array
into Go value of type interface {}"

    s := "hello"
    var v interface{} = s
    cbor.Unmarshal(data, &v)

With this change, decoder handles not-nil interface in the same way as
nil interface by storing appropraite Go type in the interface value.
This behavior is now consistent with encoding/json.

Closes: #68
  • Loading branch information
fxamacker committed Dec 12, 2019
1 parent 6f9ec24 commit d26d3cd
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 13 deletions.
6 changes: 3 additions & 3 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func (d *decodeState) value(v interface{}) error {

rv = rv.Elem()

if rv.Kind() == reflect.Interface && rv.NumMethod() == 0 && rv.IsNil() {
if rv.Kind() == reflect.Interface && rv.NumMethod() == 0 {
// Fast path to decode to empty interface without calling implementsUnmarshaler.
iv, err := d.parseInterface()
if iv != nil {
Expand Down Expand Up @@ -206,8 +206,8 @@ func (t cborType) String() string {

// parse assumes data is well-formed, and does not perform bounds checking.
func (d *decodeState) parse(v reflect.Value, isUnmarshaler bool) (err error) {
if v.Kind() == reflect.Interface && v.NumMethod() == 0 && v.IsNil() {
// nil interface
if v.Kind() == reflect.Interface && v.NumMethod() == 0 {
// empty interface
iv, err := d.parseInterface()
if err != nil {
return err
Expand Down
43 changes: 33 additions & 10 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -869,21 +869,30 @@ func TestUnmarshalIntoArray(t *testing.T) {

func TestUnmarshalNil(t *testing.T) {
cborData := [][]byte{hexDecode("f6"), hexDecode("f7")} // null, undefined
bSlice := []byte{1, 2, 3}
strSlice := []string{"hello", "world"}
m := map[string]bool{"hello": true, "goodbye": false}
nilValuesAfterUnmarshal := []interface{}{bSlice, strSlice, m}

// Test []byte/slice/map with CBOR nil/undefined
for _, data := range cborData {
for _, v := range nilValuesAfterUnmarshal {
if err := cbor.Unmarshal(data, &v); err != nil {
t.Errorf("Unmarshal(0x%0x) returns error %v", data, err)
} else if v != nil {
t.Errorf("Unmarshal(0x%0x) = %v (%T), want nil", data, v, v)
}
bSlice := []byte{1, 2, 3}
if err := cbor.Unmarshal(data, &bSlice); err != nil {
t.Errorf("Unmarshal(0x%0x) returns error %v", data, err)
} else if bSlice != nil {
t.Errorf("Unmarshal(0x%0x) = %v (%T), want nil", data, bSlice, bSlice)
}
strSlice := []string{"hello", "world"}
if err := cbor.Unmarshal(data, &strSlice); err != nil {
t.Errorf("Unmarshal(0x%0x) returns error %v", data, err)
} else if strSlice != nil {
t.Errorf("Unmarshal(0x%0x) = %v (%T), want nil", data, strSlice, strSlice)
}
m := map[string]bool{"hello": true, "goodbye": false}
if err := cbor.Unmarshal(data, &m); err != nil {
t.Errorf("Unmarshal(0x%0x) returns error %v", data, err)
} else if m != nil {
t.Errorf("Unmarshal(0x%0x) = %v (%T), want nil", data, m, m)
}
}

// Test int/float64/string/bool with CBOR nil/undefined
for _, data := range cborData {
i := 10
if err := cbor.Unmarshal(data, &i); err != nil {
Expand Down Expand Up @@ -2239,3 +2248,17 @@ func TestMapKeyUnhashable(t *testing.T) {
t.Errorf("Unmarshal(0x%0x) returns error %q, want %q", cborData, err, wantErrorMsg)
}
}

func TestUnmarshalToNotNilInterface(t *testing.T) {
cborData := hexDecode("83010203") // []uint64{1, 2, 3}
s := "hello"
var v interface{} = s // Unmarshal() sees v as type inteface{} and sets CBOR data as default Go type. s is unmodified. Same behavior as encoding/json.
wantV := []interface{}{uint64(1), uint64(2), uint64(3)}
if err := cbor.Unmarshal(cborData, &v); err != nil {
t.Errorf("Unmarshal(0x%0x) returns error %s", cborData, err)
} else if !reflect.DeepEqual(v, wantV) {
t.Errorf("Unmarshal(0x%0x) = %v (%T), want %v (%T)", cborData, v, v, wantV, wantV)
} else if s != "hello" {
t.Errorf("Unmarshal(0x%0x) modified s %s", cborData, s)
}
}

0 comments on commit d26d3cd

Please sign in to comment.