74
|
1 //
|
|
2 // Copyright (c) 2011-2019 Canonical Ltd
|
|
3 //
|
|
4 // Licensed under the Apache License, Version 2.0 (the "License");
|
|
5 // you may not use this file except in compliance with the License.
|
|
6 // You may obtain a copy of the License at
|
|
7 //
|
|
8 // http://www.apache.org/licenses/LICENSE-2.0
|
|
9 //
|
|
10 // Unless required by applicable law or agreed to in writing, software
|
|
11 // distributed under the License is distributed on an "AS IS" BASIS,
|
|
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13 // See the License for the specific language governing permissions and
|
|
14 // limitations under the License.
|
|
15
|
|
16 // Package yaml implements YAML support for the Go language.
|
|
17 //
|
|
18 // Source code and other details for the project are available at GitHub:
|
|
19 //
|
|
20 // https://github.com/go-yaml/yaml
|
|
21 //
|
|
22 package yaml
|
|
23
|
|
24 import (
|
|
25 "errors"
|
|
26 "fmt"
|
|
27 "io"
|
|
28 "reflect"
|
|
29 "strings"
|
|
30 "sync"
|
|
31 "unicode/utf8"
|
|
32 )
|
|
33
|
|
34 // The Unmarshaler interface may be implemented by types to customize their
|
|
35 // behavior when being unmarshaled from a YAML document.
|
|
36 type Unmarshaler interface {
|
|
37 UnmarshalYAML(value *Node) error
|
|
38 }
|
|
39
|
|
40 type obsoleteUnmarshaler interface {
|
|
41 UnmarshalYAML(unmarshal func(interface{}) error) error
|
|
42 }
|
|
43
|
|
44 // The Marshaler interface may be implemented by types to customize their
|
|
45 // behavior when being marshaled into a YAML document. The returned value
|
|
46 // is marshaled in place of the original value implementing Marshaler.
|
|
47 //
|
|
48 // If an error is returned by MarshalYAML, the marshaling procedure stops
|
|
49 // and returns with the provided error.
|
|
50 type Marshaler interface {
|
|
51 MarshalYAML() (interface{}, error)
|
|
52 }
|
|
53
|
|
54 // Unmarshal decodes the first document found within the in byte slice
|
|
55 // and assigns decoded values into the out value.
|
|
56 //
|
|
57 // Maps and pointers (to a struct, string, int, etc) are accepted as out
|
|
58 // values. If an internal pointer within a struct is not initialized,
|
|
59 // the yaml package will initialize it if necessary for unmarshalling
|
|
60 // the provided data. The out parameter must not be nil.
|
|
61 //
|
|
62 // The type of the decoded values should be compatible with the respective
|
|
63 // values in out. If one or more values cannot be decoded due to a type
|
|
64 // mismatches, decoding continues partially until the end of the YAML
|
|
65 // content, and a *yaml.TypeError is returned with details for all
|
|
66 // missed values.
|
|
67 //
|
|
68 // Struct fields are only unmarshalled if they are exported (have an
|
|
69 // upper case first letter), and are unmarshalled using the field name
|
|
70 // lowercased as the default key. Custom keys may be defined via the
|
|
71 // "yaml" name in the field tag: the content preceding the first comma
|
|
72 // is used as the key, and the following comma-separated options are
|
|
73 // used to tweak the marshalling process (see Marshal).
|
|
74 // Conflicting names result in a runtime error.
|
|
75 //
|
|
76 // For example:
|
|
77 //
|
|
78 // type T struct {
|
|
79 // F int `yaml:"a,omitempty"`
|
|
80 // B int
|
|
81 // }
|
|
82 // var t T
|
|
83 // yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
|
|
84 //
|
|
85 // See the documentation of Marshal for the format of tags and a list of
|
|
86 // supported tag options.
|
|
87 //
|
|
88 func Unmarshal(in []byte, out interface{}) (err error) {
|
|
89 return unmarshal(in, out, false)
|
|
90 }
|
|
91
|
|
92 // A Decoder reads and decodes YAML values from an input stream.
|
|
93 type Decoder struct {
|
|
94 parser *parser
|
|
95 knownFields bool
|
|
96 }
|
|
97
|
|
98 // NewDecoder returns a new decoder that reads from r.
|
|
99 //
|
|
100 // The decoder introduces its own buffering and may read
|
|
101 // data from r beyond the YAML values requested.
|
|
102 func NewDecoder(r io.Reader) *Decoder {
|
|
103 return &Decoder{
|
|
104 parser: newParserFromReader(r),
|
|
105 }
|
|
106 }
|
|
107
|
|
108 // KnownFields ensures that the keys in decoded mappings to
|
|
109 // exist as fields in the struct being decoded into.
|
|
110 func (dec *Decoder) KnownFields(enable bool) {
|
|
111 dec.knownFields = enable
|
|
112 }
|
|
113
|
|
114 // Decode reads the next YAML-encoded value from its input
|
|
115 // and stores it in the value pointed to by v.
|
|
116 //
|
|
117 // See the documentation for Unmarshal for details about the
|
|
118 // conversion of YAML into a Go value.
|
|
119 func (dec *Decoder) Decode(v interface{}) (err error) {
|
|
120 d := newDecoder()
|
|
121 d.knownFields = dec.knownFields
|
|
122 defer handleErr(&err)
|
|
123 node := dec.parser.parse()
|
|
124 if node == nil {
|
|
125 return io.EOF
|
|
126 }
|
|
127 out := reflect.ValueOf(v)
|
|
128 if out.Kind() == reflect.Ptr && !out.IsNil() {
|
|
129 out = out.Elem()
|
|
130 }
|
|
131 d.unmarshal(node, out)
|
|
132 if len(d.terrors) > 0 {
|
|
133 return &TypeError{d.terrors}
|
|
134 }
|
|
135 return nil
|
|
136 }
|
|
137
|
|
138 // Decode decodes the node and stores its data into the value pointed to by v.
|
|
139 //
|
|
140 // See the documentation for Unmarshal for details about the
|
|
141 // conversion of YAML into a Go value.
|
|
142 func (n *Node) Decode(v interface{}) (err error) {
|
|
143 d := newDecoder()
|
|
144 defer handleErr(&err)
|
|
145 out := reflect.ValueOf(v)
|
|
146 if out.Kind() == reflect.Ptr && !out.IsNil() {
|
|
147 out = out.Elem()
|
|
148 }
|
|
149 d.unmarshal(n, out)
|
|
150 if len(d.terrors) > 0 {
|
|
151 return &TypeError{d.terrors}
|
|
152 }
|
|
153 return nil
|
|
154 }
|
|
155
|
|
156 func unmarshal(in []byte, out interface{}, strict bool) (err error) {
|
|
157 defer handleErr(&err)
|
|
158 d := newDecoder()
|
|
159 p := newParser(in)
|
|
160 defer p.destroy()
|
|
161 node := p.parse()
|
|
162 if node != nil {
|
|
163 v := reflect.ValueOf(out)
|
|
164 if v.Kind() == reflect.Ptr && !v.IsNil() {
|
|
165 v = v.Elem()
|
|
166 }
|
|
167 d.unmarshal(node, v)
|
|
168 }
|
|
169 if len(d.terrors) > 0 {
|
|
170 return &TypeError{d.terrors}
|
|
171 }
|
|
172 return nil
|
|
173 }
|
|
174
|
|
175 // Marshal serializes the value provided into a YAML document. The structure
|
|
176 // of the generated document will reflect the structure of the value itself.
|
|
177 // Maps and pointers (to struct, string, int, etc) are accepted as the in value.
|
|
178 //
|
|
179 // Struct fields are only marshalled if they are exported (have an upper case
|
|
180 // first letter), and are marshalled using the field name lowercased as the
|
|
181 // default key. Custom keys may be defined via the "yaml" name in the field
|
|
182 // tag: the content preceding the first comma is used as the key, and the
|
|
183 // following comma-separated options are used to tweak the marshalling process.
|
|
184 // Conflicting names result in a runtime error.
|
|
185 //
|
|
186 // The field tag format accepted is:
|
|
187 //
|
|
188 // `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
|
|
189 //
|
|
190 // The following flags are currently supported:
|
|
191 //
|
|
192 // omitempty Only include the field if it's not set to the zero
|
|
193 // value for the type or to empty slices or maps.
|
|
194 // Zero valued structs will be omitted if all their public
|
|
195 // fields are zero, unless they implement an IsZero
|
|
196 // method (see the IsZeroer interface type), in which
|
|
197 // case the field will be excluded if IsZero returns true.
|
|
198 //
|
|
199 // flow Marshal using a flow style (useful for structs,
|
|
200 // sequences and maps).
|
|
201 //
|
|
202 // inline Inline the field, which must be a struct or a map,
|
|
203 // causing all of its fields or keys to be processed as if
|
|
204 // they were part of the outer struct. For maps, keys must
|
|
205 // not conflict with the yaml keys of other struct fields.
|
|
206 //
|
|
207 // In addition, if the key is "-", the field is ignored.
|
|
208 //
|
|
209 // For example:
|
|
210 //
|
|
211 // type T struct {
|
|
212 // F int `yaml:"a,omitempty"`
|
|
213 // B int
|
|
214 // }
|
|
215 // yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
|
|
216 // yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
|
|
217 //
|
|
218 func Marshal(in interface{}) (out []byte, err error) {
|
|
219 defer handleErr(&err)
|
|
220 e := newEncoder()
|
|
221 defer e.destroy()
|
|
222 e.marshalDoc("", reflect.ValueOf(in))
|
|
223 e.finish()
|
|
224 out = e.out
|
|
225 return
|
|
226 }
|
|
227
|
|
228 // An Encoder writes YAML values to an output stream.
|
|
229 type Encoder struct {
|
|
230 encoder *encoder
|
|
231 }
|
|
232
|
|
233 // NewEncoder returns a new encoder that writes to w.
|
|
234 // The Encoder should be closed after use to flush all data
|
|
235 // to w.
|
|
236 func NewEncoder(w io.Writer) *Encoder {
|
|
237 return &Encoder{
|
|
238 encoder: newEncoderWithWriter(w),
|
|
239 }
|
|
240 }
|
|
241
|
|
242 // Encode writes the YAML encoding of v to the stream.
|
|
243 // If multiple items are encoded to the stream, the
|
|
244 // second and subsequent document will be preceded
|
|
245 // with a "---" document separator, but the first will not.
|
|
246 //
|
|
247 // See the documentation for Marshal for details about the conversion of Go
|
|
248 // values to YAML.
|
|
249 func (e *Encoder) Encode(v interface{}) (err error) {
|
|
250 defer handleErr(&err)
|
|
251 e.encoder.marshalDoc("", reflect.ValueOf(v))
|
|
252 return nil
|
|
253 }
|
|
254
|
|
255 // Encode encodes value v and stores its representation in n.
|
|
256 //
|
|
257 // See the documentation for Marshal for details about the
|
|
258 // conversion of Go values into YAML.
|
|
259 func (n *Node) Encode(v interface{}) (err error) {
|
|
260 defer handleErr(&err)
|
|
261 e := newEncoder()
|
|
262 defer e.destroy()
|
|
263 e.marshalDoc("", reflect.ValueOf(v))
|
|
264 e.finish()
|
|
265 p := newParser(e.out)
|
|
266 p.textless = true
|
|
267 defer p.destroy()
|
|
268 doc := p.parse()
|
|
269 *n = *doc.Content[0]
|
|
270 return nil
|
|
271 }
|
|
272
|
|
273 // SetIndent changes the used indentation used when encoding.
|
|
274 func (e *Encoder) SetIndent(spaces int) {
|
|
275 if spaces < 0 {
|
|
276 panic("yaml: cannot indent to a negative number of spaces")
|
|
277 }
|
|
278 e.encoder.indent = spaces
|
|
279 }
|
|
280
|
|
281 // Close closes the encoder by writing any remaining data.
|
|
282 // It does not write a stream terminating string "...".
|
|
283 func (e *Encoder) Close() (err error) {
|
|
284 defer handleErr(&err)
|
|
285 e.encoder.finish()
|
|
286 return nil
|
|
287 }
|
|
288
|
|
289 func handleErr(err *error) {
|
|
290 if v := recover(); v != nil {
|
|
291 if e, ok := v.(yamlError); ok {
|
|
292 *err = e.err
|
|
293 } else {
|
|
294 panic(v)
|
|
295 }
|
|
296 }
|
|
297 }
|
|
298
|
|
299 type yamlError struct {
|
|
300 err error
|
|
301 }
|
|
302
|
|
303 func fail(err error) {
|
|
304 panic(yamlError{err})
|
|
305 }
|
|
306
|
|
307 func failf(format string, args ...interface{}) {
|
|
308 panic(yamlError{fmt.Errorf("yaml: "+format, args...)})
|
|
309 }
|
|
310
|
|
311 // A TypeError is returned by Unmarshal when one or more fields in
|
|
312 // the YAML document cannot be properly decoded into the requested
|
|
313 // types. When this error is returned, the value is still
|
|
314 // unmarshaled partially.
|
|
315 type TypeError struct {
|
|
316 Errors []string
|
|
317 }
|
|
318
|
|
319 func (e *TypeError) Error() string {
|
|
320 return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n "))
|
|
321 }
|
|
322
|
|
323 type Kind uint32
|
|
324
|
|
325 const (
|
|
326 DocumentNode Kind = 1 << iota
|
|
327 SequenceNode
|
|
328 MappingNode
|
|
329 ScalarNode
|
|
330 AliasNode
|
|
331 )
|
|
332
|
|
333 type Style uint32
|
|
334
|
|
335 const (
|
|
336 TaggedStyle Style = 1 << iota
|
|
337 DoubleQuotedStyle
|
|
338 SingleQuotedStyle
|
|
339 LiteralStyle
|
|
340 FoldedStyle
|
|
341 FlowStyle
|
|
342 )
|
|
343
|
|
344 // Node represents an element in the YAML document hierarchy. While documents
|
|
345 // are typically encoded and decoded into higher level types, such as structs
|
|
346 // and maps, Node is an intermediate representation that allows detailed
|
|
347 // control over the content being decoded or encoded.
|
|
348 //
|
|
349 // It's worth noting that although Node offers access into details such as
|
|
350 // line numbers, colums, and comments, the content when re-encoded will not
|
|
351 // have its original textual representation preserved. An effort is made to
|
|
352 // render the data plesantly, and to preserve comments near the data they
|
|
353 // describe, though.
|
|
354 //
|
|
355 // Values that make use of the Node type interact with the yaml package in the
|
|
356 // same way any other type would do, by encoding and decoding yaml data
|
|
357 // directly or indirectly into them.
|
|
358 //
|
|
359 // For example:
|
|
360 //
|
|
361 // var person struct {
|
|
362 // Name string
|
|
363 // Address yaml.Node
|
|
364 // }
|
|
365 // err := yaml.Unmarshal(data, &person)
|
|
366 //
|
|
367 // Or by itself:
|
|
368 //
|
|
369 // var person Node
|
|
370 // err := yaml.Unmarshal(data, &person)
|
|
371 //
|
|
372 type Node struct {
|
|
373 // Kind defines whether the node is a document, a mapping, a sequence,
|
|
374 // a scalar value, or an alias to another node. The specific data type of
|
|
375 // scalar nodes may be obtained via the ShortTag and LongTag methods.
|
|
376 Kind Kind
|
|
377
|
|
378 // Style allows customizing the apperance of the node in the tree.
|
|
379 Style Style
|
|
380
|
|
381 // Tag holds the YAML tag defining the data type for the value.
|
|
382 // When decoding, this field will always be set to the resolved tag,
|
|
383 // even when it wasn't explicitly provided in the YAML content.
|
|
384 // When encoding, if this field is unset the value type will be
|
|
385 // implied from the node properties, and if it is set, it will only
|
|
386 // be serialized into the representation if TaggedStyle is used or
|
|
387 // the implicit tag diverges from the provided one.
|
|
388 Tag string
|
|
389
|
|
390 // Value holds the unescaped and unquoted represenation of the value.
|
|
391 Value string
|
|
392
|
|
393 // Anchor holds the anchor name for this node, which allows aliases to point to it.
|
|
394 Anchor string
|
|
395
|
|
396 // Alias holds the node that this alias points to. Only valid when Kind is AliasNode.
|
|
397 Alias *Node
|
|
398
|
|
399 // Content holds contained nodes for documents, mappings, and sequences.
|
|
400 Content []*Node
|
|
401
|
|
402 // HeadComment holds any comments in the lines preceding the node and
|
|
403 // not separated by an empty line.
|
|
404 HeadComment string
|
|
405
|
|
406 // LineComment holds any comments at the end of the line where the node is in.
|
|
407 LineComment string
|
|
408
|
|
409 // FootComment holds any comments following the node and before empty lines.
|
|
410 FootComment string
|
|
411
|
|
412 // Line and Column hold the node position in the decoded YAML text.
|
|
413 // These fields are not respected when encoding the node.
|
|
414 Line int
|
|
415 Column int
|
|
416 }
|
|
417
|
|
418 // IsZero returns whether the node has all of its fields unset.
|
|
419 func (n *Node) IsZero() bool {
|
|
420 return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil &&
|
|
421 n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0
|
|
422 }
|
|
423
|
|
424
|
|
425 // LongTag returns the long form of the tag that indicates the data type for
|
|
426 // the node. If the Tag field isn't explicitly defined, one will be computed
|
|
427 // based on the node properties.
|
|
428 func (n *Node) LongTag() string {
|
|
429 return longTag(n.ShortTag())
|
|
430 }
|
|
431
|
|
432 // ShortTag returns the short form of the YAML tag that indicates data type for
|
|
433 // the node. If the Tag field isn't explicitly defined, one will be computed
|
|
434 // based on the node properties.
|
|
435 func (n *Node) ShortTag() string {
|
|
436 if n.indicatedString() {
|
|
437 return strTag
|
|
438 }
|
|
439 if n.Tag == "" || n.Tag == "!" {
|
|
440 switch n.Kind {
|
|
441 case MappingNode:
|
|
442 return mapTag
|
|
443 case SequenceNode:
|
|
444 return seqTag
|
|
445 case AliasNode:
|
|
446 if n.Alias != nil {
|
|
447 return n.Alias.ShortTag()
|
|
448 }
|
|
449 case ScalarNode:
|
|
450 tag, _ := resolve("", n.Value)
|
|
451 return tag
|
|
452 case 0:
|
|
453 // Special case to make the zero value convenient.
|
|
454 if n.IsZero() {
|
|
455 return nullTag
|
|
456 }
|
|
457 }
|
|
458 return ""
|
|
459 }
|
|
460 return shortTag(n.Tag)
|
|
461 }
|
|
462
|
|
463 func (n *Node) indicatedString() bool {
|
|
464 return n.Kind == ScalarNode &&
|
|
465 (shortTag(n.Tag) == strTag ||
|
|
466 (n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0)
|
|
467 }
|
|
468
|
|
469 // SetString is a convenience function that sets the node to a string value
|
|
470 // and defines its style in a pleasant way depending on its content.
|
|
471 func (n *Node) SetString(s string) {
|
|
472 n.Kind = ScalarNode
|
|
473 if utf8.ValidString(s) {
|
|
474 n.Value = s
|
|
475 n.Tag = strTag
|
|
476 } else {
|
|
477 n.Value = encodeBase64(s)
|
|
478 n.Tag = binaryTag
|
|
479 }
|
|
480 if strings.Contains(n.Value, "\n") {
|
|
481 n.Style = LiteralStyle
|
|
482 }
|
|
483 }
|
|
484
|
|
485 // --------------------------------------------------------------------------
|
|
486 // Maintain a mapping of keys to structure field indexes
|
|
487
|
|
488 // The code in this section was copied from mgo/bson.
|
|
489
|
|
490 // structInfo holds details for the serialization of fields of
|
|
491 // a given struct.
|
|
492 type structInfo struct {
|
|
493 FieldsMap map[string]fieldInfo
|
|
494 FieldsList []fieldInfo
|
|
495
|
|
496 // InlineMap is the number of the field in the struct that
|
|
497 // contains an ,inline map, or -1 if there's none.
|
|
498 InlineMap int
|
|
499
|
|
500 // InlineUnmarshalers holds indexes to inlined fields that
|
|
501 // contain unmarshaler values.
|
|
502 InlineUnmarshalers [][]int
|
|
503 }
|
|
504
|
|
505 type fieldInfo struct {
|
|
506 Key string
|
|
507 Num int
|
|
508 OmitEmpty bool
|
|
509 Flow bool
|
|
510 // Id holds the unique field identifier, so we can cheaply
|
|
511 // check for field duplicates without maintaining an extra map.
|
|
512 Id int
|
|
513
|
|
514 // Inline holds the field index if the field is part of an inlined struct.
|
|
515 Inline []int
|
|
516 }
|
|
517
|
|
518 var structMap = make(map[reflect.Type]*structInfo)
|
|
519 var fieldMapMutex sync.RWMutex
|
|
520 var unmarshalerType reflect.Type
|
|
521
|
|
522 func init() {
|
|
523 var v Unmarshaler
|
|
524 unmarshalerType = reflect.ValueOf(&v).Elem().Type()
|
|
525 }
|
|
526
|
|
527 func getStructInfo(st reflect.Type) (*structInfo, error) {
|
|
528 fieldMapMutex.RLock()
|
|
529 sinfo, found := structMap[st]
|
|
530 fieldMapMutex.RUnlock()
|
|
531 if found {
|
|
532 return sinfo, nil
|
|
533 }
|
|
534
|
|
535 n := st.NumField()
|
|
536 fieldsMap := make(map[string]fieldInfo)
|
|
537 fieldsList := make([]fieldInfo, 0, n)
|
|
538 inlineMap := -1
|
|
539 inlineUnmarshalers := [][]int(nil)
|
|
540 for i := 0; i != n; i++ {
|
|
541 field := st.Field(i)
|
|
542 if field.PkgPath != "" && !field.Anonymous {
|
|
543 continue // Private field
|
|
544 }
|
|
545
|
|
546 info := fieldInfo{Num: i}
|
|
547
|
|
548 tag := field.Tag.Get("yaml")
|
|
549 if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
|
|
550 tag = string(field.Tag)
|
|
551 }
|
|
552 if tag == "-" {
|
|
553 continue
|
|
554 }
|
|
555
|
|
556 inline := false
|
|
557 fields := strings.Split(tag, ",")
|
|
558 if len(fields) > 1 {
|
|
559 for _, flag := range fields[1:] {
|
|
560 switch flag {
|
|
561 case "omitempty":
|
|
562 info.OmitEmpty = true
|
|
563 case "flow":
|
|
564 info.Flow = true
|
|
565 case "inline":
|
|
566 inline = true
|
|
567 default:
|
|
568 return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st))
|
|
569 }
|
|
570 }
|
|
571 tag = fields[0]
|
|
572 }
|
|
573
|
|
574 if inline {
|
|
575 switch field.Type.Kind() {
|
|
576 case reflect.Map:
|
|
577 if inlineMap >= 0 {
|
|
578 return nil, errors.New("multiple ,inline maps in struct " + st.String())
|
|
579 }
|
|
580 if field.Type.Key() != reflect.TypeOf("") {
|
|
581 return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String())
|
|
582 }
|
|
583 inlineMap = info.Num
|
|
584 case reflect.Struct, reflect.Ptr:
|
|
585 ftype := field.Type
|
|
586 for ftype.Kind() == reflect.Ptr {
|
|
587 ftype = ftype.Elem()
|
|
588 }
|
|
589 if ftype.Kind() != reflect.Struct {
|
|
590 return nil, errors.New("option ,inline may only be used on a struct or map field")
|
|
591 }
|
|
592 if reflect.PtrTo(ftype).Implements(unmarshalerType) {
|
|
593 inlineUnmarshalers = append(inlineUnmarshalers, []int{i})
|
|
594 } else {
|
|
595 sinfo, err := getStructInfo(ftype)
|
|
596 if err != nil {
|
|
597 return nil, err
|
|
598 }
|
|
599 for _, index := range sinfo.InlineUnmarshalers {
|
|
600 inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...))
|
|
601 }
|
|
602 for _, finfo := range sinfo.FieldsList {
|
|
603 if _, found := fieldsMap[finfo.Key]; found {
|
|
604 msg := "duplicated key '" + finfo.Key + "' in struct " + st.String()
|
|
605 return nil, errors.New(msg)
|
|
606 }
|
|
607 if finfo.Inline == nil {
|
|
608 finfo.Inline = []int{i, finfo.Num}
|
|
609 } else {
|
|
610 finfo.Inline = append([]int{i}, finfo.Inline...)
|
|
611 }
|
|
612 finfo.Id = len(fieldsList)
|
|
613 fieldsMap[finfo.Key] = finfo
|
|
614 fieldsList = append(fieldsList, finfo)
|
|
615 }
|
|
616 }
|
|
617 default:
|
|
618 return nil, errors.New("option ,inline may only be used on a struct or map field")
|
|
619 }
|
|
620 continue
|
|
621 }
|
|
622
|
|
623 if tag != "" {
|
|
624 info.Key = tag
|
|
625 } else {
|
|
626 info.Key = strings.ToLower(field.Name)
|
|
627 }
|
|
628
|
|
629 if _, found = fieldsMap[info.Key]; found {
|
|
630 msg := "duplicated key '" + info.Key + "' in struct " + st.String()
|
|
631 return nil, errors.New(msg)
|
|
632 }
|
|
633
|
|
634 info.Id = len(fieldsList)
|
|
635 fieldsList = append(fieldsList, info)
|
|
636 fieldsMap[info.Key] = info
|
|
637 }
|
|
638
|
|
639 sinfo = &structInfo{
|
|
640 FieldsMap: fieldsMap,
|
|
641 FieldsList: fieldsList,
|
|
642 InlineMap: inlineMap,
|
|
643 InlineUnmarshalers: inlineUnmarshalers,
|
|
644 }
|
|
645
|
|
646 fieldMapMutex.Lock()
|
|
647 structMap[st] = sinfo
|
|
648 fieldMapMutex.Unlock()
|
|
649 return sinfo, nil
|
|
650 }
|
|
651
|
|
652 // IsZeroer is used to check whether an object is zero to
|
|
653 // determine whether it should be omitted when marshaling
|
|
654 // with the omitempty flag. One notable implementation
|
|
655 // is time.Time.
|
|
656 type IsZeroer interface {
|
|
657 IsZero() bool
|
|
658 }
|
|
659
|
|
660 func isZero(v reflect.Value) bool {
|
|
661 kind := v.Kind()
|
|
662 if z, ok := v.Interface().(IsZeroer); ok {
|
|
663 if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() {
|
|
664 return true
|
|
665 }
|
|
666 return z.IsZero()
|
|
667 }
|
|
668 switch kind {
|
|
669 case reflect.String:
|
|
670 return len(v.String()) == 0
|
|
671 case reflect.Interface, reflect.Ptr:
|
|
672 return v.IsNil()
|
|
673 case reflect.Slice:
|
|
674 return v.Len() == 0
|
|
675 case reflect.Map:
|
|
676 return v.Len() == 0
|
|
677 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
678 return v.Int() == 0
|
|
679 case reflect.Float32, reflect.Float64:
|
|
680 return v.Float() == 0
|
|
681 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
682 return v.Uint() == 0
|
|
683 case reflect.Bool:
|
|
684 return !v.Bool()
|
|
685 case reflect.Struct:
|
|
686 vt := v.Type()
|
|
687 for i := v.NumField() - 1; i >= 0; i-- {
|
|
688 if vt.Field(i).PkgPath != "" {
|
|
689 continue // Private field
|
|
690 }
|
|
691 if !isZero(v.Field(i)) {
|
|
692 return false
|
|
693 }
|
|
694 }
|
|
695 return true
|
|
696 }
|
|
697 return false
|
|
698 }
|