marshal: do not encode embedded structs as sub-table (#368)

Currently, the marshalling code encodes the embedded structs as sub-tables.
This is a bit unexpected, as it differs from what encoding/json does in
that case: https://play.golang.org/p/KDPaGtrijV1

Unmarshalling code handles this scenario gracefully.

This PR adapts the encoder to behave like encoding/json.
Fields in an embedded struct are promoted to the top level table.
In case the embedded struct is named in the tag, it will still
encode as a sub-table.

The added PromoteAnonymous option on the Encoder allows configuring
the old behavior, where anonymous structs are encoded as sub-tables.

On duplicate keys, the behavior of encoding/json is mimicked:
Fields from anonymous structs are shadowed by regular fields.

An example is added to show the affects of setting PromoteAnonymous.
This commit is contained in:
Oncilla
2020-04-25 17:25:56 +02:00
committed by GitHub
parent 947ab3f90a
commit d1e0fc37ce
3 changed files with 199 additions and 9 deletions
+93
View File
@@ -1974,6 +1974,99 @@ func TestUnmarshalDefaultFailureUnsupported(t *testing.T) {
}
}
func TestMarshalNestedAnonymousStructs(t *testing.T) {
type Embedded struct {
Value string `toml:"value"`
Top struct {
Value string `toml:"value"`
} `toml:"top"`
}
type Named struct {
Value string `toml:"value"`
}
var doc struct {
Embedded
Named `toml:"named"`
Anonymous struct {
Value string `toml:"value"`
} `toml:"anonymous"`
}
expected := `value = ""
[anonymous]
value = ""
[named]
value = ""
[top]
value = ""
`
result, err := Marshal(doc)
if err != nil {
t.Fatalf("unexpected error: %s", err.Error())
}
if !bytes.Equal(result, []byte(expected)) {
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, string(result))
}
}
func TestEncoderPromoteNestedAnonymousStructs(t *testing.T) {
type Embedded struct {
Value string `toml:"value"`
}
var doc struct {
Embedded
}
expected := `
[Embedded]
value = ""
`
var buf bytes.Buffer
if err := NewEncoder(&buf).PromoteAnonymous(true).Encode(doc); err != nil {
t.Fatalf("unexpected error: %s", err.Error())
}
if !bytes.Equal(buf.Bytes(), []byte(expected)) {
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, buf.String())
}
}
func TestMarshalNestedAnonymousStructs_DuplicateField(t *testing.T) {
type Embedded struct {
Value string `toml:"value"`
Top struct {
Value string `toml:"value"`
} `toml:"top"`
}
var doc struct {
Value string `toml:"value"`
Embedded
}
doc.Embedded.Value = "shadowed"
doc.Value = "shadows"
expected := `value = "shadows"
[top]
value = ""
`
result, err := Marshal(doc)
if err != nil {
t.Fatalf("unexpected error: %s", err.Error())
}
if !bytes.Equal(result, []byte(expected)) {
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, string(result))
}
}
func TestUnmarshalNestedAnonymousStructs(t *testing.T) {
type Nested struct {
Value string `toml:"nested_field"`