Support inline flag on a map field.

This commit is contained in:
Gustavo Niemeyer 2015-01-19 16:55:52 -02:00
Родитель bef53efd0c
Коммит 5d6f7e02b7
5 изменённых файлов: 69 добавлений и 12 удалений

Просмотреть файл

@ -607,6 +607,15 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
}
name := settableValueOf("")
l := len(n.children)
var inlineMap reflect.Value
var elemType reflect.Type
if sinfo.InlineMap != -1 {
inlineMap = out.Field(sinfo.InlineMap)
inlineMap.Set(reflect.New(inlineMap.Type()).Elem())
elemType = inlineMap.Type().Elem()
}
for i := 0; i < l; i += 2 {
ni := n.children[i]
if isMerge(ni) {
@ -624,6 +633,13 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
field = out.FieldByIndex(info.Inline)
}
d.unmarshal(n.children[i+1], field)
} else if sinfo.InlineMap != -1 {
if inlineMap.IsNil() {
inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
}
value := reflect.New(elemType).Elem()
d.unmarshal(n.children[i+1], value)
inlineMap.SetMapIndex(name, value)
}
}
return true

Просмотреть файл

@ -473,6 +473,15 @@ var unmarshalTests = []struct {
}{1, inlineB{2, inlineC{3}}},
},
// Map inlining
{
"a: 1\nb: 2\nc: 3\n",
&struct {
A int
C map[string]int `yaml:",inline"`
}{1, map[string]int{"b": 2, "c": 3}},
},
// bug 1243827
{
"a: -b_c",

Просмотреть файл

@ -2,6 +2,7 @@ package yaml
import (
"encoding"
"fmt"
"reflect"
"regexp"
"sort"
@ -164,6 +165,22 @@ func (e *encoder) structv(tag string, in reflect.Value) {
e.flow = info.Flow
e.marshal("", value)
}
if sinfo.InlineMap >= 0 {
m := in.Field(sinfo.InlineMap)
if m.Len() > 0 {
e.flow = false
keys := keyList(m.MapKeys())
sort.Sort(keys)
for _, k := range keys {
if _, found := sinfo.FieldsMap[k.String()]; found {
panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String()))
}
e.marshal("", k)
e.flow = false
e.marshal("", m.MapIndex(k))
}
}
}
})
}

Просмотреть файл

@ -238,6 +238,15 @@ var marshalTests = []struct {
"a: 1\nb: 2\nc: 3\n",
},
// Map inlining
{
&struct {
A int
C map[string]int `yaml:",inline"`
}{1, map[string]int{"b": 2, "c": 3}},
"a: 1\nb: 2\nc: 3\n",
},
// Duration
{
map[string]time.Duration{"a": 3 * time.Second},
@ -312,6 +321,12 @@ var marshalErrorTests = []struct {
inlineB ",inline"
}{1, inlineB{2, inlineC{3}}},
panic: `Duplicated key 'b' in struct struct \{ B int; .*`,
}, {
value: &struct {
A int
B map[string]int ",inline"
}{1, map[string]int{"a": 2}},
panic: `Can't have key "a" in inlined map; conflicts with struct field`,
}}
func (s *S) TestMarshalErrors(c *C) {

24
yaml.go
Просмотреть файл

@ -119,9 +119,10 @@ func Unmarshal(in []byte, out interface{}) (err error) {
// flow Marshal using a flow style (useful for structs,
// sequences and maps.
//
// inline Inline the struct it's applied to, so its fields
// are processed as if they were part of the outer
// struct.
// inline Inline the field, which must be a struct or a map,
// causing all of its fields or keys to be processed as if
// they were part of the outer struct. For maps, keys must
// not conflict with the yaml keys of other struct fields.
//
// In addition, if the key is "-", the field is ignored.
//
@ -255,15 +256,14 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
if inline {
switch field.Type.Kind() {
// TODO: Implement support for inline maps.
//case reflect.Map:
// if inlineMap >= 0 {
// return nil, errors.New("Multiple ,inline maps in struct " + st.String())
// }
// if field.Type.Key() != reflect.TypeOf("") {
// return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
// }
// inlineMap = info.Num
case reflect.Map:
if inlineMap >= 0 {
return nil, errors.New("Multiple ,inline maps in struct " + st.String())
}
if field.Type.Key() != reflect.TypeOf("") {
return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
}
inlineMap = info.Num
case reflect.Struct:
sinfo, err := getStructInfo(field.Type)
if err != nil {