yaml.go raw

   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