Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Squash decoder option squashes everything #193

Closed
uvw opened this issue May 21, 2020 · 0 comments · Fixed by #194
Closed

Squash decoder option squashes everything #193

uvw opened this issue May 21, 2020 · 0 comments · Fixed by #194

Comments

@uvw
Copy link
Contributor

uvw commented May 21, 2020

The squash decoder option should squash only embedded structs as documented, but it squashes all struct fields regardless of being embedded or not.

The proposed fix is trivial and was implemented in one of the similar PRs (#134): test for fieldType.Anonymous flag when this option is enabled.

However, this would be a breaking change since some users might rely on the "squash everything" behavior of the option already. I will create a PR with a straightforward fix but not sure if such a breaking change is acceptable in this repo.

Alternatively, the config.Squash option could be complemented with something along the lines of config.SquashEmbedded.

Test case to demonstrate the issue:

package mapstructure_test

import (
	"testing"

	"github.com/mitchellh/mapstructure"
)

type (
	Config struct {
		Nested        // expected to be squashed
		Named  Nested // expected to not be squashed
	}
	Nested struct {
		Value string
	}
)

func TestDecode_SquashOption(t *testing.T) {
	input := map[string]interface{}{
		"Value": "foo",
		"Named": map[string]interface{}{
			"Value": "bar",
		},
	}

	result := Config{}
	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		Result: &result,
		Squash: true,
	})
	if err != nil {
		t.Fatalf("got an err: %s", err.Error())
	}

	err = decoder.Decode(input)
	if err != nil {
		t.Fatalf("got an err: %s", err.Error())
	}

	if result.Nested.Value != "foo" {
		t.Errorf("Nested.Value value should be 'foo': %#v", result.Nested.Value)
	}
	if result.Named.Value != "bar" {
		t.Errorf("Named.Value value should be 'bar': %#v", result.Named.Value)
	}
}

func TestDecodeFrom_SquashOption(t *testing.T) {
	input := Config{
		Nested: Nested{Value: "foo"},
		Named:  Nested{Value: "bar"},
	}

	result := map[string]interface{}{}
	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		Result: &result,
		Squash: true,
	})
	if err != nil {
		t.Fatalf("got an err: %s", err.Error())
	}

	err = decoder.Decode(input)
	if err != nil {
		t.Fatalf("got an err: %s", err.Error())
	}

	if v, ok := result["Value"]; !ok {
		t.Error("Value (from an embedded field) should be present in map")
	} else if v != "foo" {
		t.Errorf("Value (from an embedded field) should be 'foo': %#v", v)
	}

	if v, ok := result["Named"]; !ok {
		t.Error("Named field should be present in map")
	} else {
		named := v.(map[string]interface{})
		if v, ok := named["Value"]; !ok {
			t.Error("Named: Value should be present in map")
		} else if v != "bar" {
			t.Errorf("Named: Value should be 'bar': %#v", v)
		}
	}
}
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant