1
// Package yaml implements YAML support for the Go language.
3
// Source code and other details for the project are available at GitHub:
5
// https://github.com/go-yaml/yaml
19
func fail(msg string) {
23
func handleErr(err *error) {
24
if r := recover(); r != nil {
25
if e, ok := r.(yamlError); ok {
26
*err = errors.New("YAML error: " + string(e))
33
// The Setter interface may be implemented by types to do their own custom
34
// unmarshalling of YAML values, rather than being implicitly assigned by
35
// the yaml package machinery. If setting the value works, the method should
36
// return true. If it returns false, the value is considered unsupported
37
// and is omitted from maps and slices.
38
type Setter interface {
39
SetYAML(tag string, value interface{}) bool
42
// The Getter interface is implemented by types to do their own custom
43
// marshalling into a YAML tag and value.
44
type Getter interface {
45
GetYAML() (tag string, value interface{})
48
// Unmarshal decodes the first document found within the in byte slice
49
// and assigns decoded values into the out value.
51
// Maps and pointers (to a struct, string, int, etc) are accepted as out
52
// values. If an internal pointer within a struct is not initialized,
53
// the yaml package will initialize it if necessary for unmarshalling
54
// the provided data. The out parameter must not be nil.
56
// The type of the decoded values and the type of out will be considered,
57
// and Unmarshal will do the best possible job to unmarshal values
58
// appropriately. It is NOT considered an error, though, to skip values
59
// because they are not available in the decoded YAML, or if they are not
60
// compatible with the out value. To ensure something was properly
61
// unmarshaled use a map or compare against the previous value for the
62
// field (usually the zero value).
64
// Struct fields are only unmarshalled if they are exported (have an
65
// upper case first letter), and are unmarshalled using the field name
66
// lowercased as the default key. Custom keys may be defined via the
67
// "yaml" name in the field tag: the content preceding the first comma
68
// is used as the key, and the following comma-separated options are
69
// used to tweak the marshalling process (see Marshal).
70
// Conflicting names result in a runtime error.
75
// F int `yaml:"a,omitempty"`
79
// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
81
// See the documentation of Marshal for the format of tags and a list of
82
// supported tag options.
84
func Unmarshal(in []byte, out interface{}) (err error) {
91
v := reflect.ValueOf(out)
92
if v.Kind() == reflect.Ptr && !v.IsNil() {
100
// Marshal serializes the value provided into a YAML document. The structure
101
// of the generated document will reflect the structure of the value itself.
102
// Maps and pointers (to struct, string, int, etc) are accepted as the in value.
104
// Struct fields are only unmarshalled if they are exported (have an upper case
105
// first letter), and are unmarshalled using the field name lowercased as the
106
// default key. Custom keys may be defined via the "yaml" name in the field
107
// tag: the content preceding the first comma is used as the key, and the
108
// following comma-separated options are used to tweak the marshalling process.
109
// Conflicting names result in a runtime error.
111
// The field tag format accepted is:
113
// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
115
// The following flags are currently supported:
117
// omitempty Only include the field if it's not set to the zero
118
// value for the type or to empty slices or maps.
119
// Does not apply to zero valued structs.
121
// flow Marshal using a flow style (useful for structs,
122
// sequences and maps.
124
// inline Inline the struct it's applied to, so its fields
125
// are processed as if they were part of the outer
128
// In addition, if the key is "-", the field is ignored.
133
// F int "a,omitempty"
136
// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
137
// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
139
func Marshal(in interface{}) (out []byte, err error) {
140
defer handleErr(&err)
143
e.marshal("", reflect.ValueOf(in))
149
// --------------------------------------------------------------------------
150
// Maintain a mapping of keys to structure field indexes
152
// The code in this section was copied from mgo/bson.
154
// structInfo holds details for the serialization of fields of
156
type structInfo struct {
157
FieldsMap map[string]fieldInfo
158
FieldsList []fieldInfo
160
// InlineMap is the number of the field in the struct that
161
// contains an ,inline map, or -1 if there's none.
165
type fieldInfo struct {
171
// Inline holds the field index if the field is part of an inlined struct.
175
var structMap = make(map[reflect.Type]*structInfo)
176
var fieldMapMutex sync.RWMutex
178
func getStructInfo(st reflect.Type) (*structInfo, error) {
179
fieldMapMutex.RLock()
180
sinfo, found := structMap[st]
181
fieldMapMutex.RUnlock()
187
fieldsMap := make(map[string]fieldInfo)
188
fieldsList := make([]fieldInfo, 0, n)
190
for i := 0; i != n; i++ {
192
if field.PkgPath != "" {
193
continue // Private field
196
info := fieldInfo{Num: i}
198
tag := field.Tag.Get("yaml")
199
if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
200
tag = string(field.Tag)
207
fields := strings.Split(tag, ",")
209
for _, flag := range fields[1:] {
212
info.OmitEmpty = true
218
return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st))
225
switch field.Type.Kind() {
226
// TODO: Implement support for inline maps.
228
// if inlineMap >= 0 {
229
// return nil, errors.New("Multiple ,inline maps in struct " + st.String())
231
// if field.Type.Key() != reflect.TypeOf("") {
232
// return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
234
// inlineMap = info.Num
236
sinfo, err := getStructInfo(field.Type)
240
for _, finfo := range sinfo.FieldsList {
241
if _, found := fieldsMap[finfo.Key]; found {
242
msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
243
return nil, errors.New(msg)
245
if finfo.Inline == nil {
246
finfo.Inline = []int{i, finfo.Num}
248
finfo.Inline = append([]int{i}, finfo.Inline...)
250
fieldsMap[finfo.Key] = finfo
251
fieldsList = append(fieldsList, finfo)
254
//return nil, errors.New("Option ,inline needs a struct value or map field")
255
return nil, errors.New("Option ,inline needs a struct value field")
263
info.Key = strings.ToLower(field.Name)
266
if _, found = fieldsMap[info.Key]; found {
267
msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
268
return nil, errors.New(msg)
271
fieldsList = append(fieldsList, info)
272
fieldsMap[info.Key] = info
275
sinfo = &structInfo{fieldsMap, fieldsList, inlineMap}
278
structMap[st] = sinfo
279
fieldMapMutex.Unlock()
283
func isZero(v reflect.Value) bool {
286
return len(v.String()) == 0
287
case reflect.Interface, reflect.Ptr:
293
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
295
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: