goyaml.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. //
  2. // goyaml - YAML support for the Go language
  3. //
  4. // https://wiki.ubuntu.com/goyaml
  5. //
  6. // Copyright (c) 2011 Canonical Ltd.
  7. //
  8. // Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com>
  9. //
  10. package goyaml
  11. import (
  12. "errors"
  13. "fmt"
  14. "reflect"
  15. "runtime"
  16. "strings"
  17. "sync"
  18. )
  19. func handleErr(err *error) {
  20. if r := recover(); r != nil {
  21. if _, ok := r.(runtime.Error); ok {
  22. panic(r)
  23. } else if _, ok := r.(*reflect.ValueError); ok {
  24. panic(r)
  25. } else if _, ok := r.(externalPanic); ok {
  26. panic(r)
  27. } else if s, ok := r.(string); ok {
  28. *err = errors.New("YAML error: " + s)
  29. } else if e, ok := r.(error); ok {
  30. *err = e
  31. } else {
  32. panic(r)
  33. }
  34. }
  35. }
  36. // Objects implementing the goyaml.Setter interface will receive the YAML
  37. // tag and value via the SetYAML method during unmarshaling, rather than
  38. // being implicitly assigned by the goyaml machinery. If setting the value
  39. // works, the method should return true. If it returns false, the given
  40. // value will be omitted from maps and slices.
  41. type Setter interface {
  42. SetYAML(tag string, value interface{}) bool
  43. }
  44. // Objects implementing the goyaml.Getter interface will get the GetYAML()
  45. // method called when goyaml is requested to marshal the given value, and
  46. // the result of this method will be marshaled in place of the actual object.
  47. type Getter interface {
  48. GetYAML() (tag string, value interface{})
  49. }
  50. // Unmarshal decodes the first document found within the in byte slice
  51. // and assigns decoded values into the object pointed by out.
  52. //
  53. // Maps, pointers to structs and ints, etc, may all be used as out values.
  54. // If an internal pointer within a struct is not initialized, goyaml
  55. // will initialize it if necessary for unmarshalling the provided data,
  56. // but the struct provided as out must not be a nil pointer.
  57. //
  58. // The type of the decoded values and the type of out will be considered,
  59. // and Unmarshal() will do the best possible job to unmarshal values
  60. // appropriately. It is NOT considered an error, though, to skip values
  61. // because they are not available in the decoded YAML, or if they are not
  62. // compatible with the out value. To ensure something was properly
  63. // unmarshaled use a map or compare against the previous value for the
  64. // field (usually the zero value).
  65. //
  66. // Struct fields are only unmarshalled if they are exported (have an
  67. // upper case first letter), and will be unmarshalled using the field
  68. // name lowercased by default. When custom field names are desired, the
  69. // tag value may be used to tweak the name. Everything before the first
  70. // comma in the field tag will be used as the name. The values following
  71. // the comma are used to tweak the marshalling process (see Marshal).
  72. //
  73. // For example:
  74. //
  75. // type T struct {
  76. // F int "a,omitempty"
  77. // B int
  78. // }
  79. // var T t
  80. // goyaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
  81. //
  82. func Unmarshal(in []byte, out interface{}) (err error) {
  83. defer handleErr(&err)
  84. d := newDecoder()
  85. p := newParser(in)
  86. defer p.destroy()
  87. node := p.parse()
  88. if node != nil {
  89. d.unmarshal(node, reflect.ValueOf(out))
  90. }
  91. return nil
  92. }
  93. // Marshal serializes the value provided into a YAML document. The structure
  94. // of the generated document will reflect the structure of the value itself.
  95. // Maps, pointers to structs and ints, etc, may all be used as the in value.
  96. //
  97. // In the case of struct values, only exported fields will be serialized.
  98. // The lowercased field name is used as the key for each exported field,
  99. // but this behavior may be changed using the respective field tag.
  100. // The tag may also contain flags to tweak the marshalling behavior for
  101. // the field. The tag formats accepted are:
  102. //
  103. // "[<key>][,<flag1>[,<flag2>]]"
  104. //
  105. // `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
  106. //
  107. // The following flags are currently supported:
  108. //
  109. // omitempty Only include the field if it's not set to the zero
  110. // value for the type or to empty slices or maps.
  111. // Does not apply to zero valued structs.
  112. //
  113. // flow Marshal using a flow style (useful for structs,
  114. // sequences and maps.
  115. //
  116. // In addition, if the key is "-", the field is ignored.
  117. //
  118. // For example:
  119. //
  120. // type T struct {
  121. // F int "a,omitempty"
  122. // B int
  123. // }
  124. // goyaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
  125. // goyaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
  126. //
  127. func Marshal(in interface{}) (out []byte, err error) {
  128. //defer handleErr(&err)
  129. e := newEncoder()
  130. defer e.destroy()
  131. e.marshal("", reflect.ValueOf(in))
  132. e.finish()
  133. out = e.out
  134. return
  135. }
  136. // --------------------------------------------------------------------------
  137. // Maintain a mapping of keys to structure field indexes
  138. // The code in this section was copied from gobson.
  139. type structFields struct {
  140. Map map[string]fieldInfo
  141. List []fieldInfo
  142. }
  143. type fieldInfo struct {
  144. Key string
  145. Num int
  146. OmitEmpty bool
  147. Flow bool
  148. }
  149. var fieldMap = make(map[reflect.Type]*structFields)
  150. var fieldMapMutex sync.RWMutex
  151. type externalPanic string
  152. func (e externalPanic) String() string {
  153. return string(e)
  154. }
  155. func getStructFields(st reflect.Type) (*structFields, error) {
  156. fieldMapMutex.RLock()
  157. fields, found := fieldMap[st]
  158. fieldMapMutex.RUnlock()
  159. if found {
  160. return fields, nil
  161. }
  162. n := st.NumField()
  163. fieldsMap := make(map[string]fieldInfo)
  164. fieldsList := make([]fieldInfo, n)
  165. for i := 0; i != n; i++ {
  166. field := st.Field(i)
  167. if field.PkgPath != "" {
  168. continue // Private field
  169. }
  170. info := fieldInfo{Num: i}
  171. tag := field.Tag.Get("yaml")
  172. if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
  173. tag = string(field.Tag)
  174. }
  175. if tag == "-" {
  176. continue
  177. }
  178. // XXX Drop this after a few releases.
  179. if s := strings.Index(tag, "/"); s >= 0 {
  180. recommend := tag[:s]
  181. for _, c := range tag[s+1:] {
  182. switch c {
  183. case 'c':
  184. recommend += ",omitempty"
  185. case 'f':
  186. recommend += ",flow"
  187. default:
  188. msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", string([]byte{uint8(c)}), tag, st)
  189. panic(externalPanic(msg))
  190. }
  191. }
  192. msg := fmt.Sprintf("Replace tag %q in field %s of type %s by %q", tag, field.Name, st, recommend)
  193. panic(externalPanic(msg))
  194. }
  195. fields := strings.Split(tag, ",")
  196. if len(fields) > 1 {
  197. for _, flag := range fields[1:] {
  198. switch flag {
  199. case "omitempty":
  200. info.OmitEmpty = true
  201. case "flow":
  202. info.Flow = true
  203. default:
  204. msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)
  205. panic(externalPanic(msg))
  206. }
  207. }
  208. tag = fields[0]
  209. }
  210. if tag != "" {
  211. info.Key = tag
  212. } else {
  213. info.Key = strings.ToLower(field.Name)
  214. }
  215. if _, found = fieldsMap[info.Key]; found {
  216. msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
  217. return nil, errors.New(msg)
  218. }
  219. fieldsList[len(fieldsMap)] = info
  220. fieldsMap[info.Key] = info
  221. }
  222. fields = &structFields{fieldsMap, fieldsList[:len(fieldsMap)]}
  223. fieldMapMutex.Lock()
  224. fieldMap[st] = fields
  225. fieldMapMutex.Unlock()
  226. return fields, nil
  227. }
  228. func isZero(v reflect.Value) bool {
  229. switch v.Kind() {
  230. case reflect.String:
  231. return len(v.String()) == 0
  232. case reflect.Interface, reflect.Ptr:
  233. return v.IsNil()
  234. case reflect.Slice:
  235. return v.Len() == 0
  236. case reflect.Map:
  237. return v.Len() == 0
  238. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  239. return v.Int() == 0
  240. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  241. return v.Uint() == 0
  242. case reflect.Bool:
  243. return !v.Bool()
  244. }
  245. return false
  246. }