Permalink
Browse files

Support inline flag on a map field.

  • Loading branch information...
1 parent bef53ef commit 5d6f7e02b7cdad63b06ab3877915532cd30073b4 @niemeyer niemeyer committed Jan 19, 2015
Showing with 69 additions and 12 deletions.
  1. +16 −0 decode.go
  2. +9 −0 decode_test.go
  3. +17 −0 encode.go
  4. +15 −0 encode_test.go
  5. +12 −12 yaml.go
View
@@ -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
View
@@ -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",
View
@@ -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))
+ }
+ }
+ }
})
}
View
@@ -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) {
View
@@ -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 {

0 comments on commit 5d6f7e0

Please sign in to comment.