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 }