豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit 8110e71

Browse files
Merge pull request #8 from go-viper/decode-remaining-fields
Fix Encoding Struct Back to Map Bug (mitchellh#279)
2 parents 9b573a2 + cd3404e commit 8110e71

File tree

4 files changed

+105
-0
lines changed

4 files changed

+105
-0
lines changed

mapstructure.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,18 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
962962
if v.Kind() != reflect.Struct {
963963
return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
964964
}
965+
} else {
966+
if strings.Index(tagValue[index+1:], "remain") != -1 {
967+
if v.Kind() != reflect.Map {
968+
return fmt.Errorf("error remain-tag field with invalid type: '%s'", v.Type())
969+
}
970+
971+
ptr := v.MapRange()
972+
for ptr.Next() {
973+
valMap.SetMapIndex(ptr.Key(), ptr.Value())
974+
}
975+
continue
976+
}
965977
}
966978
if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" {
967979
keyName = keyNameTagValue

mapstructure_benchmark_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,29 @@ func Benchmark_DecodeTagged(b *testing.B) {
283283
Decode(input, &result)
284284
}
285285
}
286+
287+
func Benchmark_DecodeWithRemainingFields(b *testing.B) {
288+
type Person struct {
289+
Name string
290+
Other map[string]interface{} `mapstructure:",remain"`
291+
}
292+
293+
input := map[string]interface{}{
294+
"name": "Luffy",
295+
"age": 19,
296+
"powers": []string{
297+
"Rubber Man",
298+
"Conqueror Haki",
299+
},
300+
}
301+
302+
for i := 0; i < b.N; i++ {
303+
// Decoding Map -> Struct
304+
var person Person
305+
_ = Decode(input, &person)
306+
307+
// Decoding Struct -> Map
308+
result := make(map[string]interface{})
309+
_ = Decode(&person, &result)
310+
}
311+
}

mapstructure_examples_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,41 @@ func ExampleDecode_remainingData() {
228228
// mapstructure.Person{Name:"Mitchell", Age:91, Other:map[string]interface {}{"email":"mitchell@example.com"}}
229229
}
230230

231+
func ExampleDecode_remainingDataDecodeBackToMapInFlatFormat() {
232+
// Note that the mapstructure tags defined in the struct type
233+
// can indicate which fields the values are mapped to.
234+
type Person struct {
235+
Name string
236+
Age int
237+
Other map[string]interface{} `mapstructure:",remain"`
238+
}
239+
240+
input := map[string]interface{}{
241+
"name": "Luffy",
242+
"age": 19,
243+
"powers": []string{
244+
"Rubber Man",
245+
"Conqueror Haki",
246+
},
247+
}
248+
249+
var person Person
250+
err := Decode(input, &person)
251+
if err != nil {
252+
panic(err)
253+
}
254+
255+
result := make(map[string]interface{})
256+
err = Decode(&person, &result)
257+
if err != nil {
258+
panic(err)
259+
}
260+
261+
fmt.Printf("%#v", result)
262+
// Output:
263+
// map[string]interface {}{"Age":19, "Name":"Luffy", "powers":[]string{"Rubber Man", "Conqueror Haki"}}
264+
}
265+
231266
func ExampleDecode_omitempty() {
232267
// Add omitempty annotation to avoid map keys for empty values
233268
type Family struct {

mapstructure_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2247,6 +2247,38 @@ func TestDecodeTable(t *testing.T) {
22472247
&map[string]interface{}{"visible": nil},
22482248
false,
22492249
},
2250+
{
2251+
"remainder with decode to map",
2252+
&Remainder{
2253+
A: "Alabasta",
2254+
Extra: map[string]interface{}{
2255+
"B": "Baratie",
2256+
"C": "Cocoyasi",
2257+
},
2258+
},
2259+
&map[string]interface{}{},
2260+
&map[string]interface{}{
2261+
"A": "Alabasta",
2262+
"B": "Baratie",
2263+
"C": "Cocoyasi",
2264+
},
2265+
false,
2266+
},
2267+
{
2268+
"remainder with decode to map with non-map field",
2269+
&struct {
2270+
A string
2271+
Extra *struct{} `mapstructure:",remain"`
2272+
}{
2273+
A: "Alabasta",
2274+
Extra: nil,
2275+
},
2276+
&map[string]interface{}{},
2277+
&map[string]interface{}{
2278+
"A": "Alabasta",
2279+
},
2280+
true,
2281+
},
22502282
}
22512283

22522284
for _, tt := range tests {

0 commit comments

Comments
 (0)