Unmarshal recursive structs (#557)

Co-authored-by: Nabetani <takenori@nabetani.sakura.ne.jp>
This commit is contained in:
Thomas Pelletier
2021-06-08 14:22:39 -04:00
committed by GitHub
parent 618f0181ac
commit 773f10110c
2 changed files with 179 additions and 0 deletions
+7
View File
@@ -370,6 +370,13 @@ func (d *decoder) handleKeyPart(key ast.Iterator, v reflect.Value, nextFn handle
// First, dispatch over v to make sure it is a valid object.
// There is no guarantee over what it could be.
switch v.Kind() {
case reflect.Ptr:
elem := v.Elem()
if !elem.IsValid() {
v.Set(reflect.New(v.Type().Elem()))
}
elem = v.Elem()
return d.handleKeyPart(key, elem, nextFn, makeFn)
case reflect.Map:
// Create the key for the map element. For now assume it's a string.
mk := reflect.ValueOf(string(key.Node().Data))
+172
View File
@@ -1,6 +1,7 @@
package toml_test
import (
"encoding/json"
"errors"
"fmt"
"math"
@@ -2120,6 +2121,177 @@ bar = 42
}
}
func TestUnmarshal_RecursiveTable(t *testing.T) {
type Foo struct {
I int
F *Foo
}
examples := []struct {
desc string
input string
expected string
err bool
}{
{
desc: "simplest",
input: `
I=1
`,
expected: `{"I":1,"F":null}`,
},
{
desc: "depth 1",
input: `
I=1
[F]
I=2
`,
expected: `{"I":1,"F":{"I":2,"F":null}}`,
},
{
desc: "depth 3",
input: `
I=1
[F]
I=2
[F.F]
I=3
`,
expected: `{"I":1,"F":{"I":2,"F":{"I":3,"F":null}}}`,
},
{
desc: "depth 4",
input: `
I=1
[F]
I=2
[F.F]
I=3
[F.F.F]
I=4
`,
expected: `{"I":1,"F":{"I":2,"F":{"I":3,"F":{"I":4,"F":null}}}}`,
},
{
desc: "skip mid step",
input: `
I=1
[F.F]
I=7
`,
expected: `{"I":1,"F":{"I":0,"F":{"I":7,"F":null}}}`,
},
}
for _, ex := range examples {
e := ex
t.Run(e.desc, func(t *testing.T) {
foo := Foo{}
err := toml.Unmarshal([]byte(e.input), &foo)
if e.err {
require.Error(t, err)
} else {
require.NoError(t, err)
j, err := json.Marshal(foo)
require.NoError(t, err)
assert.Equal(t, e.expected, string(j))
}
})
}
}
func TestUnmarshal_RecursiveTableArray(t *testing.T) {
type Foo struct {
I int
F []*Foo
}
examples := []struct {
desc string
input string
expected string
err bool
}{
{
desc: "simplest",
input: `
I=1
F=[]
`,
expected: `{"I":1,"F":[]}`,
},
{
desc: "depth 1",
input: `
I=1
[[F]]
I=2
F=[]
`,
expected: `{"I":1,"F":[{"I":2,"F":[]}]}`,
},
{
desc: "depth 2",
input: `
I=1
[[F]]
I=2
[[F.F]]
I=3
F=[]
`,
expected: `{"I":1,"F":[{"I":2,"F":[{"I":3,"F":[]}]}]}`,
},
{
desc: "depth 3",
input: `
I=1
[[F]]
I=2
[[F.F]]
I=3
[[F.F.F]]
I=4
F=[]
`,
expected: `{"I":1,"F":[{"I":2,"F":[{"I":3,"F":[{"I":4,"F":[]}]}]}]}`,
},
{
desc: "depth 4",
input: `
I=1
[[F]]
I=2
[[F.F]]
I=3
[[F.F.F]]
I=4
[[F.F.F.F]]
I=5
F=[]
`,
expected: `{"I":1,"F":[{"I":2,"F":[{"I":3,"F":[{"I":4,"F":[{"I":5,"F":[]}]}]}]}]}`,
},
}
for _, ex := range examples {
e := ex
t.Run(e.desc, func(t *testing.T) {
foo := Foo{}
err := toml.Unmarshal([]byte(e.input), &foo)
if e.err {
require.Error(t, err)
} else {
require.NoError(t, err)
j, err := json.Marshal(foo)
require.NoError(t, err)
assert.Equal(t, e.expected, string(j))
}
})
}
}
func ExampleDecoder_SetStrict() {
type S struct {
Key1 string