get.porter.sh/porter@v1.3.0/pkg/yaml/map.go (about)

     1  // Copyright (c) 2015-2016 Michael Persson
     2  // Copyright (c) 2012–2015 Elasticsearch <http://www.elastic.co>
     3  //
     4  // Originally distributed as part of "beats" repository (https://github.com/elastic/beats).
     5  // Modified specifically for "iodatafmt" package.
     6  // Modified to make UnmarshalYAML reusable by other types, turn null keys to "null" and be compatible with gopkg.in/yaml.v3
     7  //
     8  // Distributed underneath "Apache License, Version 2.0" which is compatible with the LICENSE for this package.
     9  // see https://github.com/go-yaml/yaml/issues/139#issuecomment-220072190
    10  
    11  package yaml
    12  
    13  import (
    14  	"fmt"
    15  )
    16  
    17  // UnmarshalMap allows unmarshaling into types that are safe to then be marshaled to json.
    18  // By default yaml serializer converts these to map[interface{}]interface which cannot be serialised as json
    19  // see https://github.com/go-yaml/yaml/issues/139
    20  // This is still required even with v3 because if any key isn't parsed as a string, e.g. you have a key named 1 or 2, then the bug is still triggered.
    21  func UnmarshalMap(unmarshal func(interface{}) error) (map[string]interface{}, error) {
    22  	var raw map[string]interface{}
    23  	err := unmarshal(&raw)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	for k, v := range raw {
    29  		raw[stringKey(k)] = cleanupMapValue(v)
    30  	}
    31  
    32  	return raw, nil
    33  }
    34  
    35  func cleanupInterfaceArray(in []interface{}) []interface{} {
    36  	res := make([]interface{}, len(in))
    37  	for i, v := range in {
    38  		res[i] = cleanupMapValue(v)
    39  	}
    40  	return res
    41  }
    42  
    43  func cleanupInterfaceMap(in map[interface{}]interface{}) map[string]interface{} {
    44  	res := make(map[string]interface{})
    45  	for k, v := range in {
    46  		res[stringKey(k)] = cleanupMapValue(v)
    47  	}
    48  	return res
    49  }
    50  
    51  func cleanupStringMap(in map[string]interface{}) map[string]interface{} {
    52  	res := make(map[string]interface{})
    53  	for k, v := range in {
    54  		res[stringKey(k)] = cleanupMapValue(v)
    55  	}
    56  	return res
    57  }
    58  
    59  func cleanupMapValue(v interface{}) interface{} {
    60  	switch v := v.(type) {
    61  	case []interface{}:
    62  		return cleanupInterfaceArray(v)
    63  	case map[interface{}]interface{}:
    64  		return cleanupInterfaceMap(v)
    65  	case map[string]interface{}:
    66  		return cleanupStringMap(v)
    67  	default:
    68  		return v
    69  	}
    70  }
    71  
    72  func stringKey(k interface{}) string {
    73  	switch k {
    74  	case nil:
    75  		return "null"
    76  	default:
    77  		return fmt.Sprintf("%v", k)
    78  	}
    79  }