1 package yaml
2 3 import (
4 "bytes"
5 "context"
6 "io"
7 8 "github.com/goccy/go-yaml/ast"
9 "github.com/goccy/go-yaml/internal/errors"
10 "golang.org/x/xerrors"
11 )
12 13 // BytesMarshaler interface may be implemented by types to customize their
14 // behavior when being marshaled into a YAML document. The returned value
15 // is marshaled in place of the original value implementing Marshaler.
16 //
17 // If an error is returned by MarshalYAML, the marshaling procedure stops
18 // and returns with the provided error.
19 type BytesMarshaler interface {
20 MarshalYAML() ([]byte, error)
21 }
22 23 // BytesMarshalerContext interface use BytesMarshaler with context.Context.
24 type BytesMarshalerContext interface {
25 MarshalYAML(context.Context) ([]byte, error)
26 }
27 28 // InterfaceMarshaler interface has MarshalYAML compatible with github.com/go-yaml/yaml package.
29 type InterfaceMarshaler interface {
30 MarshalYAML() (interface{}, error)
31 }
32 33 // InterfaceMarshalerContext interface use InterfaceMarshaler with context.Context.
34 type InterfaceMarshalerContext interface {
35 MarshalYAML(context.Context) (interface{}, error)
36 }
37 38 // BytesUnmarshaler interface may be implemented by types to customize their
39 // behavior when being unmarshaled from a YAML document.
40 type BytesUnmarshaler interface {
41 UnmarshalYAML([]byte) error
42 }
43 44 // BytesUnmarshalerContext interface use BytesUnmarshaler with context.Context.
45 type BytesUnmarshalerContext interface {
46 UnmarshalYAML(context.Context, []byte) error
47 }
48 49 // InterfaceUnmarshaler interface has UnmarshalYAML compatible with github.com/go-yaml/yaml package.
50 type InterfaceUnmarshaler interface {
51 UnmarshalYAML(func(interface{}) error) error
52 }
53 54 // InterfaceUnmarshalerContext interface use InterfaceUnmarshaler with context.Context.
55 type InterfaceUnmarshalerContext interface {
56 UnmarshalYAML(context.Context, func(interface{}) error) error
57 }
58 59 // MapItem is an item in a MapSlice.
60 type MapItem struct {
61 Key, Value interface{}
62 }
63 64 // MapSlice encodes and decodes as a YAML map.
65 // The order of keys is preserved when encoding and decoding.
66 type MapSlice []MapItem
67 68 // ToMap convert to map[interface{}]interface{}.
69 func (s MapSlice) ToMap() map[interface{}]interface{} {
70 v := map[interface{}]interface{}{}
71 for _, item := range s {
72 v[item.Key] = item.Value
73 }
74 return v
75 }
76 77 // Marshal serializes the value provided into a YAML document. The structure
78 // of the generated document will reflect the structure of the value itself.
79 // Maps and pointers (to struct, string, int, etc) are accepted as the in value.
80 //
81 // Struct fields are only marshalled if they are exported (have an upper case
82 // first letter), and are marshalled using the field name lowercased as the
83 // default key. Custom keys may be defined via the "yaml" name in the field
84 // tag: the content preceding the first comma is used as the key, and the
85 // following comma-separated options are used to tweak the marshalling process.
86 // Conflicting names result in a runtime error.
87 //
88 // The field tag format accepted is:
89 //
90 // `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
91 //
92 // The following flags are currently supported:
93 //
94 // omitempty Only include the field if it's not set to the zero
95 // value for the type or to empty slices or maps.
96 // Zero valued structs will be omitted if all their public
97 // fields are zero, unless they implement an IsZero
98 // method (see the IsZeroer interface type), in which
99 // case the field will be included if that method returns true.
100 //
101 // flow Marshal using a flow style (useful for structs,
102 // sequences and maps).
103 //
104 // inline Inline the field, which must be a struct or a map,
105 // causing all of its fields or keys to be processed as if
106 // they were part of the outer struct. For maps, keys must
107 // not conflict with the yaml keys of other struct fields.
108 //
109 // anchor Marshal with anchor. If want to define anchor name explicitly, use anchor=name style.
110 // Otherwise, if used 'anchor' name only, used the field name lowercased as the anchor name
111 //
112 // alias Marshal with alias. If want to define alias name explicitly, use alias=name style.
113 // Otherwise, If omitted alias name and the field type is pointer type,
114 // assigned anchor name automatically from same pointer address.
115 //
116 // In addition, if the key is "-", the field is ignored.
117 //
118 // For example:
119 //
120 // type T struct {
121 // F int `yaml:"a,omitempty"`
122 // B int
123 // }
124 // yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
125 // yaml.Marshal(&T{F: 1}) // Returns "a: 1\nb: 0\n"
126 //
127 func Marshal(v interface{}) ([]byte, error) {
128 return MarshalWithOptions(v)
129 }
130 131 // MarshalWithOptions serializes the value provided into a YAML document with EncodeOptions.
132 func MarshalWithOptions(v interface{}, opts ...EncodeOption) ([]byte, error) {
133 return MarshalContext(context.Background(), v, opts...)
134 }
135 136 // MarshalContext serializes the value provided into a YAML document with context.Context and EncodeOptions.
137 func MarshalContext(ctx context.Context, v interface{}, opts ...EncodeOption) ([]byte, error) {
138 var buf bytes.Buffer
139 if err := NewEncoder(&buf, opts...).EncodeContext(ctx, v); err != nil {
140 return nil, errors.Wrapf(err, "failed to marshal")
141 }
142 return buf.Bytes(), nil
143 }
144 145 // ValueToNode convert from value to ast.Node.
146 func ValueToNode(v interface{}, opts ...EncodeOption) (ast.Node, error) {
147 var buf bytes.Buffer
148 node, err := NewEncoder(&buf, opts...).EncodeToNode(v)
149 if err != nil {
150 return nil, errors.Wrapf(err, "failed to convert value to node")
151 }
152 return node, nil
153 }
154 155 // Unmarshal decodes the first document found within the in byte slice
156 // and assigns decoded values into the out value.
157 //
158 // Struct fields are only unmarshalled if they are exported (have an
159 // upper case first letter), and are unmarshalled using the field name
160 // lowercased as the default key. Custom keys may be defined via the
161 // "yaml" name in the field tag: the content preceding the first comma
162 // is used as the key, and the following comma-separated options are
163 // used to tweak the marshalling process (see Marshal).
164 // Conflicting names result in a runtime error.
165 //
166 // For example:
167 //
168 // type T struct {
169 // F int `yaml:"a,omitempty"`
170 // B int
171 // }
172 // var t T
173 // yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
174 //
175 // See the documentation of Marshal for the format of tags and a list of
176 // supported tag options.
177 //
178 func Unmarshal(data []byte, v interface{}) error {
179 return UnmarshalWithOptions(data, v)
180 }
181 182 // UnmarshalWithOptions decodes with DecodeOptions the first document found within the in byte slice
183 // and assigns decoded values into the out value.
184 func UnmarshalWithOptions(data []byte, v interface{}, opts ...DecodeOption) error {
185 return UnmarshalContext(context.Background(), data, v, opts...)
186 }
187 188 // UnmarshalContext decodes with context.Context and DecodeOptions.
189 func UnmarshalContext(ctx context.Context, data []byte, v interface{}, opts ...DecodeOption) error {
190 dec := NewDecoder(bytes.NewBuffer(data), opts...)
191 if err := dec.DecodeContext(ctx, v); err != nil {
192 if err == io.EOF {
193 return nil
194 }
195 return errors.Wrapf(err, "failed to unmarshal")
196 }
197 return nil
198 }
199 200 // NodeToValue converts node to the value pointed to by v.
201 func NodeToValue(node ast.Node, v interface{}, opts ...DecodeOption) error {
202 var buf bytes.Buffer
203 if err := NewDecoder(&buf, opts...).DecodeFromNode(node, v); err != nil {
204 return errors.Wrapf(err, "failed to convert node to value")
205 }
206 return nil
207 }
208 209 // FormatError is a utility function that takes advantage of the metadata
210 // stored in the errors returned by this package's parser.
211 //
212 // If the second argument `colored` is true, the error message is colorized.
213 // If the third argument `inclSource` is true, the error message will
214 // contain snippets of the YAML source that was used.
215 func FormatError(e error, colored, inclSource bool) string {
216 var pp errors.PrettyPrinter
217 if xerrors.As(e, &pp) {
218 var buf bytes.Buffer
219 pp.PrettyPrint(&errors.Sink{&buf}, colored, inclSource)
220 return buf.String()
221 }
222 223 return e.Error()
224 }
225 226 // YAMLToJSON convert YAML bytes to JSON.
227 func YAMLToJSON(bytes []byte) ([]byte, error) {
228 var v interface{}
229 if err := UnmarshalWithOptions(bytes, &v, UseOrderedMap()); err != nil {
230 return nil, errors.Wrapf(err, "failed to unmarshal")
231 }
232 out, err := MarshalWithOptions(v, JSON())
233 if err != nil {
234 return nil, errors.Wrapf(err, "failed to marshal with json option")
235 }
236 return out, nil
237 }
238 239 // JSONToYAML convert JSON bytes to YAML.
240 func JSONToYAML(bytes []byte) ([]byte, error) {
241 var v interface{}
242 if err := UnmarshalWithOptions(bytes, &v, UseOrderedMap()); err != nil {
243 return nil, errors.Wrapf(err, "failed to unmarshal from json bytes")
244 }
245 out, err := Marshal(v)
246 if err != nil {
247 return nil, errors.Wrapf(err, "failed to marshal")
248 }
249 return out, nil
250 }
251