From 581da5dff9505e412bfe00824610b3431bf72014 Mon Sep 17 00:00:00 2001 From: Chris Broglie Date: Mon, 16 Mar 2015 01:29:23 -0700 Subject: [PATCH] Support decoding into reflect.Value instances This is useful if you have a generic method which is trying to decode a slice of structs individually. Since you can't pass an object of type []myStruct to a method expecting []interface{}, the only way to handle it is to have the method signature accept just interface{}, and use reflect to access the individual elements. --- mapstructure.go | 17 ++++++++++++----- mapstructure_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/mapstructure.go b/mapstructure.go index 381ba5d4..c83a8704 100644 --- a/mapstructure.go +++ b/mapstructure.go @@ -125,12 +125,15 @@ func WeakDecode(input, output interface{}) error { // a decoder has been returned, the same configuration must not be used // again. func NewDecoder(config *DecoderConfig) (*Decoder, error) { - val := reflect.ValueOf(config.Result) - if val.Kind() != reflect.Ptr { - return nil, errors.New("result must be a pointer") + val, ok := config.Result.(reflect.Value) + if !ok { + val = reflect.ValueOf(config.Result) + if val.Kind() != reflect.Ptr { + return nil, errors.New("result must be a pointer") + } + val = val.Elem() } - val = val.Elem() if !val.CanAddr() { return nil, errors.New("result must be addressable (a pointer)") } @@ -159,7 +162,11 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) { // Decode decodes the given raw interface to the target pointer specified // by the configuration. func (d *Decoder) Decode(raw interface{}) error { - return d.decode("", raw, reflect.ValueOf(d.config.Result).Elem()) + val, ok := d.config.Result.(reflect.Value) + if !ok { + val = reflect.ValueOf(d.config.Result).Elem() + } + return d.decode("", raw, val) } // Decodes an unknown data type into a specific reflection value. diff --git a/mapstructure_test.go b/mapstructure_test.go index 036e6b5f..bbc66b73 100644 --- a/mapstructure_test.go +++ b/mapstructure_test.go @@ -184,6 +184,33 @@ func TestDecode_Embedded(t *testing.T) { } } +func TestDecode_EmbeddedWithReflect(t *testing.T) { + t.Parallel() + + input := map[string]interface{}{ + "vstring": "foo", + "Basic": map[string]interface{}{ + "vstring": "innerfoo", + }, + "vunique": "bar", + } + + result := make([]Embedded, 1) + rv := reflect.ValueOf(result) + err := Decode(input, rv.Index(0)) + if err != nil { + t.Fatalf("got an err: %s", err.Error()) + } + + if result[0].Vstring != "innerfoo" { + t.Errorf("vstring value should be 'innerfoo': %#v", result[0].Vstring) + } + + if result[0].Vunique != "bar" { + t.Errorf("vunique value should be 'bar': %#v", result[0].Vunique) + } +} + func TestDecode_EmbeddedPointer(t *testing.T) { t.Parallel()