Skip to content

Commit 2842dc8

Browse files
Merge pull request #18 from triarius/viper-squash-name
Add configuration option to specify tag value that indicates squash
2 parents cafeac4 + aa74cdc commit 2842dc8

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

mapstructure.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,10 @@ type DecoderConfig struct {
266266
// defaults to "mapstructure"
267267
TagName string
268268

269+
// The option of the value in the tag that indicates a field should
270+
// be squashed. This defaults to "squash".
271+
SquashTagOption string
272+
269273
// IgnoreUntaggedFields ignores all struct fields without explicit
270274
// TagName, comparable to `mapstructure:"-"` as default behaviour.
271275
IgnoreUntaggedFields bool
@@ -402,6 +406,10 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
402406
config.TagName = "mapstructure"
403407
}
404408

409+
if config.SquashTagOption == "" {
410+
config.SquashTagOption = "squash"
411+
}
412+
405413
if config.MatchName == nil {
406414
config.MatchName = strings.EqualFold
407415
}
@@ -977,7 +985,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
977985
}
978986

979987
// If "squash" is specified in the tag, we squash the field down.
980-
squash = squash || strings.Index(tagValue[index+1:], "squash") != -1
988+
squash = squash || strings.Contains(tagValue[index+1:], d.config.SquashTagOption)
981989
if squash {
982990
// When squashing, the embedded type can be a pointer to a struct.
983991
if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
@@ -1355,7 +1363,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
13551363
// We always parse the tags cause we're looking for other tags too
13561364
tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")
13571365
for _, tag := range tagParts[1:] {
1358-
if tag == "squash" {
1366+
if tag == d.config.SquashTagOption {
13591367
squash = true
13601368
break
13611369
}

mapstructure_test.go

+60
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ type BasicSquash struct {
5151
Test Basic `mapstructure:",squash"`
5252
}
5353

54+
type BasicJSONInline struct {
55+
Test Basic `json:",inline"`
56+
}
57+
5458
type Embedded struct {
5559
Basic
5660
Vunique string
@@ -489,6 +493,62 @@ func TestDecodeFrom_BasicSquash(t *testing.T) {
489493
}
490494
}
491495

496+
func TestDecode_BasicJSONInline(t *testing.T) {
497+
t.Parallel()
498+
499+
input := map[string]interface{}{
500+
"vstring": "foo",
501+
}
502+
503+
var result BasicJSONInline
504+
d, err := NewDecoder(&DecoderConfig{TagName: "json", SquashTagOption: "inline", Result: &result})
505+
if err != nil {
506+
t.Fatalf("got an err: %s", err.Error())
507+
}
508+
509+
if err := d.Decode(input); err != nil {
510+
t.Fatalf("got an err: %s", err.Error())
511+
}
512+
513+
if result.Test.Vstring != "foo" {
514+
t.Errorf("vstring value should be 'foo': %#v", result.Test.Vstring)
515+
}
516+
}
517+
518+
func TestDecodeFrom_BasicJSONInline(t *testing.T) {
519+
t.Parallel()
520+
521+
var v interface{}
522+
var ok bool
523+
524+
input := BasicJSONInline{
525+
Test: Basic{
526+
Vstring: "foo",
527+
},
528+
}
529+
530+
var result map[string]interface{}
531+
d, err := NewDecoder(&DecoderConfig{TagName: "json", SquashTagOption: "inline", Result: &result})
532+
if err != nil {
533+
t.Fatalf("got an err: %s", err.Error())
534+
}
535+
536+
if err := d.Decode(input); err != nil {
537+
t.Fatalf("got an err: %s", err.Error())
538+
}
539+
540+
if _, ok = result["Test"]; ok {
541+
t.Error("test should not be present in map")
542+
}
543+
544+
v, ok = result["Vstring"]
545+
if !ok {
546+
t.Error("vstring should be present in map")
547+
} else if !reflect.DeepEqual(v, "foo") {
548+
t.Errorf("vstring value should be 'foo': %#v", v)
549+
}
550+
}
551+
492552
func TestDecode_Embedded(t *testing.T) {
493553
t.Parallel()
494554

0 commit comments

Comments
 (0)