github.com/microsoft/moc@v0.17.1/pkg/marshal/marshal.go (about)

     1  // Copyright (c) Microsoft Corporation.
     2  // Licensed under the Apache v2.0 license.
     3  package marshal
     4  
     5  import (
     6  	"bytes"
     7  	"crypto/sha512"
     8  	"encoding/base64"
     9  	"encoding/json"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"reflect"
    13  	"sort"
    14  
    15  	"gopkg.in/yaml.v3"
    16  )
    17  
    18  func Duplicate(data interface{}, duplicatedData interface{}) error {
    19  	dataBytes, err := ToJSONBytes(data)
    20  	if err != nil {
    21  		return err
    22  	}
    23  	err = FromJSONBytes(dataBytes, duplicatedData)
    24  	if err != nil {
    25  		return err
    26  	}
    27  	return nil
    28  }
    29  func ToString(data interface{}) string {
    30  	yamlStr, _ := ToYAML(data)
    31  	return yamlStr
    32  }
    33  
    34  func ToJSON(data interface{}) (string, error) {
    35  	jsonBytes, err := ToJSONBytes(data)
    36  	if err != nil {
    37  		return "", err
    38  	}
    39  	return string(jsonBytes), nil
    40  }
    41  func ToJSONBytes(data interface{}) ([]byte, error) {
    42  	return json.Marshal(data)
    43  }
    44  
    45  func ToPrettyPrintedJSONBytes(data interface{}) ([]byte, error) {
    46  	return json.MarshalIndent(data, "", "    ")
    47  }
    48  
    49  // json.Marshal writes some characters (e.g. '<') as unicode. This stops that to make logsa easier to read.
    50  func ToUnescapedJSONBytes(data interface{}) ([]byte, error) {
    51  	var buffer bytes.Buffer
    52  	e := json.NewEncoder(&buffer)
    53  	e.SetEscapeHTML(false)
    54  	err := e.Encode(data)
    55  	return buffer.Bytes(), err
    56  }
    57  
    58  // ToJSONFile writes the data to path in YAML format
    59  func ToJSONFile(data interface{}, path string) error {
    60  	enc, err := ToJSONBytes(data)
    61  	if err != nil {
    62  		return err
    63  
    64  	}
    65  
    66  	err = ioutil.WriteFile(path, enc, 0644)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	return nil
    71  }
    72  
    73  func FromJSON(jsonString string, object interface{}) error {
    74  	return json.Unmarshal([]byte(jsonString), object)
    75  }
    76  
    77  func FromJSONBytes(jsonBytes []byte, object interface{}) error {
    78  	return json.Unmarshal(jsonBytes, object)
    79  }
    80  
    81  func FromJSONFile(path string, object interface{}) error {
    82  	contents, err := ioutil.ReadFile(path)
    83  	if err != nil {
    84  		return err
    85  	}
    86  	return FromJSONBytes(contents, object)
    87  }
    88  
    89  func ToBase64(data string) string {
    90  	return base64.StdEncoding.EncodeToString([]byte(data))
    91  }
    92  
    93  func FromBase64(data string) ([]byte, error) {
    94  	return base64.StdEncoding.DecodeString(data)
    95  }
    96  
    97  func ToBase64URL(data string) string {
    98  	return base64.URLEncoding.EncodeToString([]byte(data))
    99  }
   100  
   101  func FromBase64URL(data string) ([]byte, error) {
   102  	return base64.URLEncoding.DecodeString(data)
   103  }
   104  
   105  func ToYAML(data interface{}) (string, error) {
   106  	yamlBytes, err := yaml.Marshal(data)
   107  	if err != nil {
   108  		return "", err
   109  	}
   110  	return string(yamlBytes), nil
   111  }
   112  func ToYAMLBytes(data interface{}) ([]byte, error) {
   113  	return yaml.Marshal(data)
   114  }
   115  
   116  func FingerprintObject(data interface{}) (*[]byte, error) {
   117  	yamlBytes, err := ToJSONBytes(data)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	// Generate the figerprint
   123  	sum := sha512.Sum512(yamlBytes)
   124  	sumSlice := sum[:]
   125  	return &sumSlice, nil
   126  }
   127  
   128  // ToYAMLFile writes the data to path in YAML format
   129  func ToYAMLFile(data interface{}, path string) error {
   130  	enc, err := ToYAMLBytes(data)
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	err = ioutil.WriteFile(path, enc, 0644)
   136  	if err != nil {
   137  		return err
   138  	}
   139  	return nil
   140  }
   141  
   142  func FromYAMLBytes(yamlData []byte, object interface{}) error {
   143  	return yaml.Unmarshal(yamlData, object)
   144  }
   145  
   146  func FromYAMLString(yamlString string, object interface{}) error {
   147  	return FromYAMLBytes([]byte(yamlString), object)
   148  }
   149  
   150  func FromYAMLFile(path string, object interface{}) error {
   151  	contents, err := ioutil.ReadFile(path)
   152  	if err != nil {
   153  		return err
   154  	}
   155  	return FromYAMLBytes(contents, object)
   156  }
   157  
   158  func ToTSV(data interface{}) (string, error) {
   159  	jsonBytes, err := ToTSVBytes(data)
   160  	if err != nil {
   161  		return "", err
   162  	}
   163  	return string(jsonBytes), nil
   164  }
   165  
   166  func ToTSVBytes(data interface{}) ([]byte, error) {
   167  	return marshalTSV(data)
   168  }
   169  
   170  func marshalTSV(result interface{}) ([]byte, error) {
   171  	var bytes []byte
   172  	items := reflect.ValueOf(result)
   173  	if items.Kind() == reflect.Slice {
   174  		for i := 0; i < items.Len(); i++ {
   175  			str, err := marshalOneTSVElement(items.Index(i).Interface())
   176  			if err != nil {
   177  				return nil, err
   178  			}
   179  			bytes = append(bytes, str...)
   180  			if i < items.Len()-1 {
   181  				bytes = append(bytes, '\n')
   182  			}
   183  		}
   184  	} else {
   185  		str, err := marshalOneTSVElement(result)
   186  		if err != nil {
   187  			return nil, err
   188  		}
   189  		bytes = append(bytes, str...)
   190  	}
   191  
   192  	return bytes, nil
   193  }
   194  
   195  func marshalOneTSVElement(result interface{}) ([]byte, error) {
   196  	var str []byte
   197  	switch v := result.(type) {
   198  	case string:
   199  		str = []byte(v)
   200  	case map[string]interface{}:
   201  		var tabString string
   202  
   203  		// golang maps purposely store keys and values in a random order.
   204  		// The order typically changes from one map instance to another.
   205  		// In order to provide result consistency, we first sort keys
   206  		// alphabetically then get the associated values
   207  		keys := make([]string, 0, len(v))
   208  		for k := range v {
   209  			keys = append(keys, k)
   210  		}
   211  		sort.Strings(keys)
   212  		for i, key := range keys {
   213  			typ, ok := v[key].(string)
   214  			if ok && typ != "" {
   215  				tabString += typ
   216  				if i < len(keys)-1 {
   217  					tabString += "\t"
   218  				}
   219  			}
   220  		}
   221  		str = []byte(tabString)
   222  	default:
   223  		return nil, fmt.Errorf("Unsupported Format")
   224  	}
   225  	return str, nil
   226  }
   227  
   228  func ToCSV(data interface{}) (string, error) {
   229  	jsonBytes, err := marshalCSV(data)
   230  	if err != nil {
   231  		return "", err
   232  	}
   233  	return string(jsonBytes), nil
   234  }
   235  
   236  func ToCSVBytes(data interface{}) ([]byte, error) {
   237  	return marshalCSV(data)
   238  }
   239  
   240  func marshalCSV(result interface{}) ([]byte, error) {
   241  	var bytes []byte
   242  	items := reflect.ValueOf(result)
   243  	if items.Kind() == reflect.Slice {
   244  		for i := 0; i < items.Len(); i++ {
   245  			str, err := marshalOneCSVElement(items.Index(i).Interface())
   246  			if err != nil {
   247  				return nil, err
   248  			}
   249  			bytes = append(bytes, str...)
   250  			if i < items.Len()-1 {
   251  				bytes = append(bytes, '\n')
   252  			}
   253  		}
   254  	} else {
   255  		str, err := marshalOneCSVElement(result)
   256  		if err != nil {
   257  			return nil, err
   258  		}
   259  		bytes = append(bytes, str...)
   260  	}
   261  
   262  	return bytes, nil
   263  }
   264  
   265  func marshalOneCSVElement(result interface{}) ([]byte, error) {
   266  	var str []byte
   267  	switch v := result.(type) {
   268  	case string:
   269  		str = []byte(v)
   270  	case map[string]interface{}:
   271  		var tabString string
   272  
   273  		// golang maps purposely store keys and values in a random order.
   274  		// The order typically changes from one map instance to another.
   275  		// In order to provide result consistency, we first sort keys
   276  		// alphabetically then get the associated values
   277  		keys := make([]string, 0, len(v))
   278  		for k := range v {
   279  			keys = append(keys, k)
   280  		}
   281  		sort.Strings(keys)
   282  		for i, key := range keys {
   283  			typ, ok := v[key].(string)
   284  			if ok && typ != "" {
   285  				tabString += typ
   286  				if i < len(keys)-1 {
   287  					tabString += ","
   288  				}
   289  			}
   290  		}
   291  		str = []byte(tabString)
   292  	default:
   293  		return nil, fmt.Errorf("Unsupported Format")
   294  	}
   295  	return str, nil
   296  }