//
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package yaml implements YAML support for the Go language. // // Source code and other details for the project are available at GitHub: // // https://github.com/go-yaml/yaml //
package yaml import ( ) // The Unmarshaler interface may be implemented by types to customize their // behavior when being unmarshaled from a YAML document. type Unmarshaler interface { UnmarshalYAML(value *Node) error } type obsoleteUnmarshaler interface { UnmarshalYAML(unmarshal func(interface{}) error) error } // The Marshaler interface may be implemented by types to customize their // behavior when being marshaled into a YAML document. The returned value // is marshaled in place of the original value implementing Marshaler. // // If an error is returned by MarshalYAML, the marshaling procedure stops // and returns with the provided error. type Marshaler interface { MarshalYAML() (interface{}, error) } // Unmarshal decodes the first document found within the in byte slice // and assigns decoded values into the out value. // // Maps and pointers (to a struct, string, int, etc) are accepted as out // values. If an internal pointer within a struct is not initialized, // the yaml package will initialize it if necessary for unmarshalling // the provided data. The out parameter must not be nil. // // The type of the decoded values should be compatible with the respective // values in out. If one or more values cannot be decoded due to a type // mismatches, decoding continues partially until the end of the YAML // content, and a *yaml.TypeError is returned with details for all // missed values. // // Struct fields are only unmarshalled if they are exported (have an // upper case first letter), and are unmarshalled using the field name // lowercased as the default key. Custom keys may be defined via the // "yaml" name in the field tag: the content preceding the first comma // is used as the key, and the following comma-separated options are // used to tweak the marshalling process (see Marshal). // Conflicting names result in a runtime error. // // For example: // // type T struct { // F int `yaml:"a,omitempty"` // B int // } // var t T // yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) // // See the documentation of Marshal for the format of tags and a list of // supported tag options. // func ( []byte, interface{}) ( error) { return unmarshal(, , false) } // A Decoder reads and decodes YAML values from an input stream. type Decoder struct { parser *parser knownFields bool } // NewDecoder returns a new decoder that reads from r. // // The decoder introduces its own buffering and may read // data from r beyond the YAML values requested. func ( io.Reader) *Decoder { return &Decoder{ parser: newParserFromReader(), } } // KnownFields ensures that the keys in decoded mappings to // exist as fields in the struct being decoded into. func ( *Decoder) ( bool) { .knownFields = } // Decode reads the next YAML-encoded value from its input // and stores it in the value pointed to by v. // // See the documentation for Unmarshal for details about the // conversion of YAML into a Go value. func ( *Decoder) ( interface{}) ( error) { := newDecoder() .knownFields = .knownFields defer handleErr(&) := .parser.parse() if == nil { return io.EOF } := reflect.ValueOf() if .Kind() == reflect.Ptr && !.IsNil() { = .Elem() } .unmarshal(, ) if len(.terrors) > 0 { return &TypeError{.terrors} } return nil } // Decode decodes the node and stores its data into the value pointed to by v. // // See the documentation for Unmarshal for details about the // conversion of YAML into a Go value. func ( *Node) ( interface{}) ( error) { := newDecoder() defer handleErr(&) := reflect.ValueOf() if .Kind() == reflect.Ptr && !.IsNil() { = .Elem() } .unmarshal(, ) if len(.terrors) > 0 { return &TypeError{.terrors} } return nil } func unmarshal( []byte, interface{}, bool) ( error) { defer handleErr(&) := newDecoder() := newParser() defer .destroy() := .parse() if != nil { := reflect.ValueOf() if .Kind() == reflect.Ptr && !.IsNil() { = .Elem() } .unmarshal(, ) } if len(.terrors) > 0 { return &TypeError{.terrors} } return nil } // Marshal serializes the value provided into a YAML document. The structure // of the generated document will reflect the structure of the value itself. // Maps and pointers (to struct, string, int, etc) are accepted as the in value. // // Struct fields are only marshalled if they are exported (have an upper case // first letter), and are marshalled using the field name lowercased as the // default key. Custom keys may be defined via the "yaml" name in the field // tag: the content preceding the first comma is used as the key, and the // following comma-separated options are used to tweak the marshalling process. // Conflicting names result in a runtime error. // // The field tag format accepted is: // // `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)` // // The following flags are currently supported: // // omitempty Only include the field if it's not set to the zero // value for the type or to empty slices or maps. // Zero valued structs will be omitted if all their public // fields are zero, unless they implement an IsZero // method (see the IsZeroer interface type), in which // case the field will be excluded if IsZero returns true. // // flow Marshal using a flow style (useful for structs, // sequences and maps). // // 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. // // For example: // // type T struct { // F int `yaml:"a,omitempty"` // B int // } // yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" // yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" // func ( interface{}) ( []byte, error) { defer handleErr(&) := newEncoder() defer .destroy() .marshalDoc("", reflect.ValueOf()) .finish() = .out return } // An Encoder writes YAML values to an output stream. type Encoder struct { encoder *encoder } // NewEncoder returns a new encoder that writes to w. // The Encoder should be closed after use to flush all data // to w. func ( io.Writer) *Encoder { return &Encoder{ encoder: newEncoderWithWriter(), } } // Encode writes the YAML encoding of v to the stream. // If multiple items are encoded to the stream, the // second and subsequent document will be preceded // with a "---" document separator, but the first will not. // // See the documentation for Marshal for details about the conversion of Go // values to YAML. func ( *Encoder) ( interface{}) ( error) { defer handleErr(&) .encoder.marshalDoc("", reflect.ValueOf()) return nil } // Encode encodes value v and stores its representation in n. // // See the documentation for Marshal for details about the // conversion of Go values into YAML. func ( *Node) ( interface{}) ( error) { defer handleErr(&) := newEncoder() defer .destroy() .marshalDoc("", reflect.ValueOf()) .finish() := newParser(.out) .textless = true defer .destroy() := .parse() * = *.Content[0] return nil } // SetIndent changes the used indentation used when encoding. func ( *Encoder) ( int) { if < 0 { panic("yaml: cannot indent to a negative number of spaces") } .encoder.indent = } // Close closes the encoder by writing any remaining data. // It does not write a stream terminating string "...". func ( *Encoder) () ( error) { defer handleErr(&) .encoder.finish() return nil } func handleErr( *error) { if := recover(); != nil { if , := .(yamlError); { * = .err } else { panic() } } } type yamlError struct { err error } func fail( error) { panic(yamlError{}) } func failf( string, ...interface{}) { panic(yamlError{fmt.Errorf("yaml: "+, ...)}) } // A TypeError is returned by Unmarshal when one or more fields in // the YAML document cannot be properly decoded into the requested // types. When this error is returned, the value is still // unmarshaled partially. type TypeError struct { Errors []string } func ( *TypeError) () string { return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(.Errors, "\n ")) } type Kind uint32 const ( DocumentNode Kind = 1 << iota SequenceNode MappingNode ScalarNode AliasNode ) type Style uint32 const ( TaggedStyle Style = 1 << iota DoubleQuotedStyle SingleQuotedStyle LiteralStyle FoldedStyle FlowStyle ) // Node represents an element in the YAML document hierarchy. While documents // are typically encoded and decoded into higher level types, such as structs // and maps, Node is an intermediate representation that allows detailed // control over the content being decoded or encoded. // // It's worth noting that although Node offers access into details such as // line numbers, colums, and comments, the content when re-encoded will not // have its original textual representation preserved. An effort is made to // render the data plesantly, and to preserve comments near the data they // describe, though. // // Values that make use of the Node type interact with the yaml package in the // same way any other type would do, by encoding and decoding yaml data // directly or indirectly into them. // // For example: // // var person struct { // Name string // Address yaml.Node // } // err := yaml.Unmarshal(data, &person) // // Or by itself: // // var person Node // err := yaml.Unmarshal(data, &person) // type Node struct { // Kind defines whether the node is a document, a mapping, a sequence, // a scalar value, or an alias to another node. The specific data type of // scalar nodes may be obtained via the ShortTag and LongTag methods. Kind Kind // Style allows customizing the apperance of the node in the tree. Style Style // Tag holds the YAML tag defining the data type for the value. // When decoding, this field will always be set to the resolved tag, // even when it wasn't explicitly provided in the YAML content. // When encoding, if this field is unset the value type will be // implied from the node properties, and if it is set, it will only // be serialized into the representation if TaggedStyle is used or // the implicit tag diverges from the provided one. Tag string // Value holds the unescaped and unquoted represenation of the value. Value string // Anchor holds the anchor name for this node, which allows aliases to point to it. Anchor string // Alias holds the node that this alias points to. Only valid when Kind is AliasNode. Alias *Node // Content holds contained nodes for documents, mappings, and sequences. Content []*Node // HeadComment holds any comments in the lines preceding the node and // not separated by an empty line. HeadComment string // LineComment holds any comments at the end of the line where the node is in. LineComment string // FootComment holds any comments following the node and before empty lines. FootComment string // Line and Column hold the node position in the decoded YAML text. // These fields are not respected when encoding the node. Line int Column int } // IsZero returns whether the node has all of its fields unset. func ( *Node) () bool { return .Kind == 0 && .Style == 0 && .Tag == "" && .Value == "" && .Anchor == "" && .Alias == nil && .Content == nil && .HeadComment == "" && .LineComment == "" && .FootComment == "" && .Line == 0 && .Column == 0 } // LongTag returns the long form of the tag that indicates the data type for // the node. If the Tag field isn't explicitly defined, one will be computed // based on the node properties. func ( *Node) () string { return longTag(.ShortTag()) } // ShortTag returns the short form of the YAML tag that indicates data type for // the node. If the Tag field isn't explicitly defined, one will be computed // based on the node properties. func ( *Node) () string { if .indicatedString() { return strTag } if .Tag == "" || .Tag == "!" { switch .Kind { case MappingNode: return mapTag case SequenceNode: return seqTag case AliasNode: if .Alias != nil { return .Alias.() } case ScalarNode: , := resolve("", .Value) return case 0: // Special case to make the zero value convenient. if .IsZero() { return nullTag } } return "" } return shortTag(.Tag) } func ( *Node) () bool { return .Kind == ScalarNode && (shortTag(.Tag) == strTag || (.Tag == "" || .Tag == "!") && .Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0) } // SetString is a convenience function that sets the node to a string value // and defines its style in a pleasant way depending on its content. func ( *Node) ( string) { .Kind = ScalarNode if utf8.ValidString() { .Value = .Tag = strTag } else { .Value = encodeBase64() .Tag = binaryTag } if strings.Contains(.Value, "\n") { .Style = LiteralStyle } } // -------------------------------------------------------------------------- // Maintain a mapping of keys to structure field indexes // The code in this section was copied from mgo/bson. // structInfo holds details for the serialization of fields of // a given struct. type structInfo struct { FieldsMap map[string]fieldInfo FieldsList []fieldInfo // InlineMap is the number of the field in the struct that // contains an ,inline map, or -1 if there's none. InlineMap int // InlineUnmarshalers holds indexes to inlined fields that // contain unmarshaler values. InlineUnmarshalers [][]int } type fieldInfo struct { Key string Num int OmitEmpty bool Flow bool // Id holds the unique field identifier, so we can cheaply // check for field duplicates without maintaining an extra map. Id int // Inline holds the field index if the field is part of an inlined struct. Inline []int } var structMap = make(map[reflect.Type]*structInfo) var fieldMapMutex sync.RWMutex var unmarshalerType reflect.Type func init() { var Unmarshaler unmarshalerType = reflect.ValueOf(&).Elem().Type() } func getStructInfo( reflect.Type) (*structInfo, error) { fieldMapMutex.RLock() , := structMap[] fieldMapMutex.RUnlock() if { return , nil } := .NumField() := make(map[string]fieldInfo) := make([]fieldInfo, 0, ) := -1 := [][]int(nil) for := 0; != ; ++ { := .Field() if .PkgPath != "" && !.Anonymous { continue // Private field } := fieldInfo{Num: } := .Tag.Get("yaml") if == "" && strings.Index(string(.Tag), ":") < 0 { = string(.Tag) } if == "-" { continue } := false := strings.Split(, ",") if len() > 1 { for , := range [1:] { switch { case "omitempty": .OmitEmpty = true case "flow": .Flow = true case "inline": = true default: return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", , , )) } } = [0] } if { switch .Type.Kind() { case reflect.Map: if >= 0 { return nil, errors.New("multiple ,inline maps in struct " + .String()) } if .Type.Key() != reflect.TypeOf("") { return nil, errors.New("option ,inline needs a map with string keys in struct " + .String()) } = .Num case reflect.Struct, reflect.Ptr: := .Type for .Kind() == reflect.Ptr { = .Elem() } if .Kind() != reflect.Struct { return nil, errors.New("option ,inline may only be used on a struct or map field") } if reflect.PtrTo().Implements(unmarshalerType) { = append(, []int{}) } else { , := () if != nil { return nil, } for , := range .InlineUnmarshalers { = append(, append([]int{}, ...)) } for , := range .FieldsList { if , := [.Key]; { := "duplicated key '" + .Key + "' in struct " + .String() return nil, errors.New() } if .Inline == nil { .Inline = []int{, .Num} } else { .Inline = append([]int{}, .Inline...) } .Id = len() [.Key] = = append(, ) } } default: return nil, errors.New("option ,inline may only be used on a struct or map field") } continue } if != "" { .Key = } else { .Key = strings.ToLower(.Name) } if _, = [.Key]; { := "duplicated key '" + .Key + "' in struct " + .String() return nil, errors.New() } .Id = len() = append(, ) [.Key] = } = &structInfo{ FieldsMap: , FieldsList: , InlineMap: , InlineUnmarshalers: , } fieldMapMutex.Lock() structMap[] = fieldMapMutex.Unlock() return , nil } // IsZeroer is used to check whether an object is zero to // determine whether it should be omitted when marshaling // with the omitempty flag. One notable implementation // is time.Time. type IsZeroer interface { IsZero() bool } func isZero( reflect.Value) bool { := .Kind() if , := .Interface().(IsZeroer); { if ( == reflect.Ptr || == reflect.Interface) && .IsNil() { return true } return .IsZero() } switch { case reflect.String: return len(.String()) == 0 case reflect.Interface, reflect.Ptr: return .IsNil() case reflect.Slice: return .Len() == 0 case reflect.Map: return .Len() == 0 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return .Int() == 0 case reflect.Float32, reflect.Float64: return .Float() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return .Uint() == 0 case reflect.Bool: return !.Bool() case reflect.Struct: := .Type() for := .NumField() - 1; >= 0; -- { if .Field().PkgPath != "" { continue // Private field } if !(.Field()) { return false } } return true } return false }