Skip to content

Commit

Permalink
added @luxas' comments, reworked tests and added lots of testcases
Browse files Browse the repository at this point in the history
Signed-off-by: Inteon <42113979+inteon@users.noreply.github.com>
  • Loading branch information
inteon committed Oct 29, 2021
1 parent e3faee5 commit 5f9c00c
Show file tree
Hide file tree
Showing 3 changed files with 583 additions and 401 deletions.
9 changes: 6 additions & 3 deletions marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import (
"gopkg.in/yaml.v2"
)

// JSONToYAML Converts JSON to YAML.
// JSONToYAML converts JSON to YAML. Notable implementation details:
//
// - Duplicate fields, are case-sensitively ignored in an undefined order.
// - The sequence indentation style is compact, which means that the "- " marker for a YAML sequence will be on the same indentation level as the sequence field name.
// - Unlike Unmarshal, all integers, up to 64 bits, are preserved during this round-trip.
func JSONToYAML(j []byte) ([]byte, error) {
// Convert the JSON to an object.
var jsonObj interface{}
Expand All @@ -31,8 +35,7 @@ func JSONToYAML(j []byte) ([]byte, error) {
return yamlBytes, nil
}

// Marshal marshals the object into JSON then converts JSON to YAML and returns the
// YAML.
// Marshal marshals obj into JSON using stdlib json.Marshal, and then converts JSON to YAML using JSONToYAML (see that method for more reference)
func Marshal(obj interface{}) ([]byte, error) {
jsonBytes, err := json.Marshal(obj)
if err != nil {
Expand Down
37 changes: 26 additions & 11 deletions unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,21 @@ func yamlToJSONTarget(yamlBytes []byte, jsonTarget *reflect.Value, unmarshalFn f
// YAMLToJSON converts YAML to JSON. Since JSON is a subset of YAML,
// passing JSON through this method should be a no-op.
//
// Things YAML can do that are not supported by JSON:
// Some things YAML can do that are not supported by JSON:
// * In YAML you can have binary and null keys in your maps. These are invalid
// in JSON. (int and float keys are converted to strings.)
// in JSON, and therefore int, bool and float keys are converted to strings implicitly.
// * Binary data in YAML with the !!binary tag is not supported. If you want to
// use binary data with this library, encode the data as base64 as usual but do
// not use the !!binary tag in your YAML. This will ensure the original base64
// encoded data makes it all the way through to the JSON.
// * And more... read the YAML specification for more details.
//
// For strict decoding of YAML, use YAMLToJSONStrict.
// Notable about the implementation:
//
// - Duplicate fields are case-sensitively ignored in an undefined order. Note that the YAML specification forbids duplicate fields, so this logic is more permissive than it needs to. See YAMLToJSONStrict for an alternative.
// - As per the YAML 1.1 specification, which yaml.v2 used underneath implements, literal 'yes' and 'no' strings without quotation marks will be converted to true/false implicitly.
// - Unlike Unmarshal, all integers, up to 64 bits, are preserved during this round-trip.
// - There are no compatibility guarantees for returned error values.
func YAMLToJSON(y []byte) ([]byte, error) {
return yamlToJSONTarget(y, nil, yaml.Unmarshal)
}
Expand All @@ -70,10 +76,7 @@ func jsonUnmarshal(reader io.Reader, obj interface{}, opts ...JSONOpt) error {
for _, opt := range opts {
d = opt(d)
}
if err := d.Decode(&obj); err != nil {
return err
}
return nil
return d.Decode(obj)
}

// unmarshal unmarshals the given YAML byte stream into the given interface,
Expand All @@ -97,14 +100,26 @@ func unmarshal(yamlBytes []byte, obj interface{}, unmarshalFn func([]byte, inter
return nil
}

// Unmarshal converts YAML to JSON then uses JSON to unmarshal into an object,
// optionally configuring the behavior of the JSON unmarshal.
// Unmarshal first converts the given YAML to JSON, and then unmarshals the JSON into obj. Options for the
// standard library json.Decoder can be optionally specified, e.g. to decode untyped numbers into json.Number instead of float64, or to disallow unknown fields (but for that purpose, see also UnmarshalStrict). obj must be a non-nil pointer.
//
// Important notes about the Unmarshal logic:
//
// - Decoding is case-insensitive, unlike the rest of Kubernetes API machinery, as this is using the stdlib json library. This might be confusing to users.
// - This decodes any number (although it is an integer) into a float64 if the type of obj is unknown, e.g. *map[string]interface{}, *interface{}, or *[]interface{}. This means integers above +/- 2^53 will lose precision when round-tripping. Make a JSONOpt that calls d.UseNumber() to avoid this.
// - Duplicate fields, including in-case-sensitive matches, are ignored in an undefined order. Note that the YAML specification forbids duplicate fields, so this logic is more permissive than it needs to. See UnmarshalStrict for an alternative.
// - Unknown fields, i.e. serialized data that do not map to a field in obj, are ignored. Use d.DisallowUnknownFields() or UnmarshalStrict to override.
// - As per the YAML 1.1 specification, which yaml.v2 used underneath implements, literal 'yes' and 'no' strings without quotation marks will be converted to true/false implicitly.
// - YAML non-string keys, e.g. ints, bools and floats, are converted to strings implicitly during the YAML to JSON conversion process.
// - There are no compatibility guarantees for returned error values.
func Unmarshal(yamlBytes []byte, obj interface{}, opts ...JSONOpt) error {
return unmarshal(yamlBytes, obj, yaml.Unmarshal, opts...)
}

// UnmarshalStrict strictly converts YAML to JSON then uses JSON to unmarshal
// into an object, optionally configuring the behavior of the JSON unmarshal.
// UnmarshalStrict is similar to Unmarshal (please read its documentation for reference), with the following exceptions:
//
// - Duplicate fields in an object yield an error. This is according to the YAML specification.
// - If obj, or any of its recursive children, is a struct, presence of fields in the serialized data unknown to the struct will yield an error.
func UnmarshalStrict(yamlBytes []byte, obj interface{}, opts ...JSONOpt) error {
return unmarshal(yamlBytes, obj, yaml.UnmarshalStrict, append(opts, DisallowUnknownFields)...)
}
Expand Down
Loading

0 comments on commit 5f9c00c

Please sign in to comment.