Просмотр исходного кода

Support inline flag on a map field.

Gustavo Niemeyer 11 лет назад
Родитель
Сommit
5d6f7e02b7
5 измененных файлов с 69 добавлено и 12 удалено
  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

+ 16 - 0
decode.go

@@ -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

+ 9 - 0
decode_test.go

@@ -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",

+ 17 - 0
encode.go

@@ -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))
+				}
+			}
+		}
 	})
 }
 

+ 15 - 0
encode_test.go

@@ -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) {

+ 12 - 12
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 {