Skip to content

Commit

Permalink
If interface value is not addressable, make copy and set
Browse files Browse the repository at this point in the history
This fixes #187. If the value of an interface is not addressable, we
can't decode into it because we can't replace any keys. To work around
this, we copy the value, decode into that, and then replace the full
value.
  • Loading branch information
mitchellh committed Jun 7, 2020
1 parent 5ffcd79 commit 93663c4
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 1 deletion.
29 changes: 28 additions & 1 deletion mapstructure.go
Expand Up @@ -463,7 +463,34 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
// value to "data" of that type.
func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error {
if val.IsValid() && val.Elem().IsValid() {
return d.decode(name, data, val.Elem())
elem := val.Elem()

// If we can't address this element, then its not writable. Instead,
// we make a copy of the value (which is a pointer and therefore
// writable), decode into that, and replace the whole value.
copied := false
if !elem.CanAddr() {
copied = true

// Make *T
copy := reflect.New(elem.Type())

// *T = elem
copy.Elem().Set(elem)

// Set elem so we decode into it
elem = copy
}

// Decode. If we have an error then return. We also return right
// away if we're not a copy because that means we decoded directly.
if err := d.decode(name, data, elem); err != nil || !copied {
return err
}

// If we're a copy, we need to set te final result
val.Set(elem.Elem())
return nil
}

dataVal := reflect.ValueOf(data)
Expand Down
22 changes: 22 additions & 0 deletions mapstructure_test.go
Expand Up @@ -375,6 +375,28 @@ func TestBasic_interfaceStruct(t *testing.T) {
}
}

// Issue 187
func TestBasic_interfaceStructNonPtr(t *testing.T) {
t.Parallel()

input := map[string]interface{}{
"vstring": "foo",
}

var iface interface{} = Basic{}
err := Decode(input, &iface)
if err != nil {
t.Fatalf("got an err: %s", err)
}

expected := Basic{
Vstring: "foo",
}
if !reflect.DeepEqual(iface, expected) {
t.Fatalf("bad: %#v", iface)
}
}

func TestDecode_BasicSquash(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit 93663c4

Please sign in to comment.