Unmarshal recursive structs (#557)
Co-authored-by: Nabetani <takenori@nabetani.sakura.ne.jp>
This commit is contained in:
@@ -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.
|
// First, dispatch over v to make sure it is a valid object.
|
||||||
// There is no guarantee over what it could be.
|
// There is no guarantee over what it could be.
|
||||||
switch v.Kind() {
|
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:
|
case reflect.Map:
|
||||||
// Create the key for the map element. For now assume it's a string.
|
// Create the key for the map element. For now assume it's a string.
|
||||||
mk := reflect.ValueOf(string(key.Node().Data))
|
mk := reflect.ValueOf(string(key.Node().Data))
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package toml_test
|
package toml_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"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() {
|
func ExampleDecoder_SetStrict() {
|
||||||
type S struct {
|
type S struct {
|
||||||
Key1 string
|
Key1 string
|
||||||
|
|||||||
Reference in New Issue
Block a user