github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/vault/sdk/helper/jsonutil/json.go (about) 1 package jsonutil 2 3 import ( 4 "bytes" 5 "compress/gzip" 6 "encoding/json" 7 "fmt" 8 "io" 9 10 "github.com/hashicorp/errwrap" 11 "github.com/hashicorp/terraform-plugin-sdk/internal/vault/sdk/helper/compressutil" 12 ) 13 14 // Encodes/Marshals the given object into JSON 15 func EncodeJSON(in interface{}) ([]byte, error) { 16 if in == nil { 17 return nil, fmt.Errorf("input for encoding is nil") 18 } 19 var buf bytes.Buffer 20 enc := json.NewEncoder(&buf) 21 if err := enc.Encode(in); err != nil { 22 return nil, err 23 } 24 return buf.Bytes(), nil 25 } 26 27 // EncodeJSONAndCompress encodes the given input into JSON and compresses the 28 // encoded value (using Gzip format BestCompression level, by default). A 29 // canary byte is placed at the beginning of the returned bytes for the logic 30 // in decompression method to identify compressed input. 31 func EncodeJSONAndCompress(in interface{}, config *compressutil.CompressionConfig) ([]byte, error) { 32 if in == nil { 33 return nil, fmt.Errorf("input for encoding is nil") 34 } 35 36 // First JSON encode the given input 37 encodedBytes, err := EncodeJSON(in) 38 if err != nil { 39 return nil, err 40 } 41 42 if config == nil { 43 config = &compressutil.CompressionConfig{ 44 Type: compressutil.CompressionTypeGzip, 45 GzipCompressionLevel: gzip.BestCompression, 46 } 47 } 48 49 return compressutil.Compress(encodedBytes, config) 50 } 51 52 // DecodeJSON tries to decompress the given data. The call to decompress, fails 53 // if the content was not compressed in the first place, which is identified by 54 // a canary byte before the compressed data. If the data is not compressed, it 55 // is JSON decoded directly. Otherwise the decompressed data will be JSON 56 // decoded. 57 func DecodeJSON(data []byte, out interface{}) error { 58 if data == nil || len(data) == 0 { 59 return fmt.Errorf("'data' being decoded is nil") 60 } 61 if out == nil { 62 return fmt.Errorf("output parameter 'out' is nil") 63 } 64 65 // Decompress the data if it was compressed in the first place 66 decompressedBytes, uncompressed, err := compressutil.Decompress(data) 67 if err != nil { 68 return errwrap.Wrapf("failed to decompress JSON: {{err}}", err) 69 } 70 if !uncompressed && (decompressedBytes == nil || len(decompressedBytes) == 0) { 71 return fmt.Errorf("decompressed data being decoded is invalid") 72 } 73 74 // If the input supplied failed to contain the compression canary, it 75 // will be notified by the compression utility. Decode the decompressed 76 // input. 77 if !uncompressed { 78 data = decompressedBytes 79 } 80 81 return DecodeJSONFromReader(bytes.NewReader(data), out) 82 } 83 84 // Decodes/Unmarshals the given io.Reader pointing to a JSON, into a desired object 85 func DecodeJSONFromReader(r io.Reader, out interface{}) error { 86 if r == nil { 87 return fmt.Errorf("'io.Reader' being decoded is nil") 88 } 89 if out == nil { 90 return fmt.Errorf("output parameter 'out' is nil") 91 } 92 93 dec := json.NewDecoder(r) 94 95 // While decoding JSON values, interpret the integer values as `json.Number`s instead of `float64`. 96 dec.UseNumber() 97 98 // Since 'out' is an interface representing a pointer, pass it to the decoder without an '&' 99 return dec.Decode(out) 100 }