From 628f91ffc4c5682fed2d354bcaf5070f3bdb6284 Mon Sep 17 00:00:00 2001 From: Mathijs van den Worm Date: Fri, 20 May 2022 13:39:34 +0200 Subject: [PATCH 1/2] Allow types to determine how to decode themselves --- mapstructure.go | 11 +++++++++++ mapstructure_examples_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/mapstructure.go b/mapstructure.go index 7581806a..6244568a 100644 --- a/mapstructure.go +++ b/mapstructure.go @@ -453,6 +453,13 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e return nil } + if outVal.CanAddr() { + v := outVal.Addr() + if u, ok := v.Interface().(decoder); ok { + return u.Decode(input) + } + } + if d.config.DecodeHook != nil { // We have a DecodeHook, so let's pre-process the input. var err error @@ -1540,3 +1547,7 @@ func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Val } return v } + +type decoder interface { + Decode(interface{}) error +} diff --git a/mapstructure_examples_test.go b/mapstructure_examples_test.go index 2413b694..5541bc47 100644 --- a/mapstructure_examples_test.go +++ b/mapstructure_examples_test.go @@ -254,3 +254,32 @@ func ExampleDecode_omitempty() { // Output: // &map[Age:0 FirstName:Somebody] } + +type Someone struct { + Name string +} + +func (p *Someone) Decode(v interface{}) error { + if name, ok := v.(string); ok { + p.Name = name + return nil + } + return fmt.Errorf("undecipherable name") +} + +func ExampleDecode_custom_decoder_type() { + type Profile struct { + Friends []Someone + } + input := map[string]interface{}{ + "friends": []string{"Mitchell"}, + } + var result Profile + err := Decode(input, &result) + if err != nil { + panic(err) + } + fmt.Printf("%#v", result) + // Output: + // mapstructure.Profile{Friends:[]mapstructure.Someone{mapstructure.Someone{Name:"Mitchell"}}} +} From b836ed75ba309ea28541a81bb3749af1e5f960fc Mon Sep 17 00:00:00 2001 From: Mathijs van den Worm Date: Thu, 15 Dec 2022 15:27:23 +0100 Subject: [PATCH 2/2] Make the custom decoder more specific to mapstructure Similar to the `UnarshalX` family of functions seen throughout `encoding` and `encoding/json`, include the name of what is being decoded into the function. --- mapstructure.go | 4 ++-- mapstructure_examples_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mapstructure.go b/mapstructure.go index 6244568a..52623e59 100644 --- a/mapstructure.go +++ b/mapstructure.go @@ -456,7 +456,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e if outVal.CanAddr() { v := outVal.Addr() if u, ok := v.Interface().(decoder); ok { - return u.Decode(input) + return u.DecodeMapstructure(input) } } @@ -1549,5 +1549,5 @@ func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Val } type decoder interface { - Decode(interface{}) error + DecodeMapstructure(interface{}) error } diff --git a/mapstructure_examples_test.go b/mapstructure_examples_test.go index 5541bc47..915cbe8c 100644 --- a/mapstructure_examples_test.go +++ b/mapstructure_examples_test.go @@ -259,7 +259,7 @@ type Someone struct { Name string } -func (p *Someone) Decode(v interface{}) error { +func (p *Someone) DecodeMapstructure(v interface{}) error { if name, ok := v.(string); ok { p.Name = name return nil