I am making a API request and the response I get back is a bytes.Buffer. I then json decode that into my struct:
resp, status, err := // api call that calls http client do and returns byte buff
result := MyStruct{}
err = json.NewDecoder(response).Decode(&result)
I know want to take my struct, result, and gzip it.
Should I be using a json decoder to get the value back?
I want to then take that decoded json so I can then gzip it eventually.
Also, I am confused between a byte array, bytebuffer and then these readers. Is this hierarchy like java?
I am a bit confused by your question but maybe this helps a bit:
Assuming you would use the standard http.Client
your HTTP call would be done via Client.Do
which returns an *http.Response
.
You can read the response body from the Body
field which is of type io.ReadCloser
. This is actually just an interface that combines the io.Reader
and io.Closer
interface. If you know the response is json
you can now create a json.Decoder
using json.NewDecoder
which accepts any io.Reader
.
Its important to keep in mind that all types implicitly implement io.Reader
by having the following function defined on them:
Read(p []byte) (n int, err error)
Just as the *http.Response
Body
field is an io.Reader
any bytes.Buffer
implements io.Reader
because it implements the Buffer.Read
function.
In contrast a []byte
(byte array) is a scalar type that does not implement any functions on its own. Therefore []byte
does not implement io.Reader
so you can not just pass this into json.NewDecoder
. If you want to decode JSON from a byte array/slice you should probably just use json.Unmarshal
or create a bytes.Buffer
from your []byte
using bytes.NewBuffer
and then again pass that to the json.Decoder
.
The same concepts apply for encoding JSON back but this time instead of an io.Reader
you need an io.Writer
and a json.Encoder
.
io.Reader
and io.Writer
are interfaces specifying object behaviour regardless of it implementation. bytes.Buffer
is data structure implementing both io.Reader
and io.Writer
. array is just core language data structure similar with others languages have. Most interfaces advantage is you can operate with them uniformly despite underlying implementations. For example io library has func TeeReader(r Reader, w Writer) Reader
which returns a Reader that writes to w what it reads from r. You can use it to gzip response as you read and decode it.
SomeWriter, err := os.OpenFile("some/File", os.O_WRONLY, os.ModePerm ) //gzip to file for example
gzipper := gzip.NewWriter(SomeWriter) //but can be any Writer
tee := io.TeeReader(response, gzipper)
//and then
err = json.NewDecoder(tee).Decode(&result)
If you want to store your struct as json, the simplest way is usually to use json.Marshal()
, as in:
b, err := json.Marshal(&myStruct)
b will in this case be a byte slice ([]byte). This can later be gzipped using the gzip
package. For instance, to gzip the bytes to a file, you could use:
f, _ := os.Create("/tmp/s.gz")
defer f.Close()
w := gzip.NewWriter(f)
w.Write(b)
w.Close()
If you want to, you can bypass creating the byte slice by using json.Encoder.Encode()
directly instead.
f, _ := os.Create("/tmp/s.gz")
defer f.Close()
w := gzip.NewWriter(f)
json.NewEncoder(w).Encode(&myStruct)
w.Close()
Depending on where you want to store or send the gzipped json, you can replace the parameter f
used in gzip.NewWriter(f)
to be any object that implements io.Writer
. For instance, you can send the gzipped response using http.ResponseWriter
directly in a handler:
func MyHandler(w http.ResponseWriter, r *http.Request) {
myStruct := ... // Get struct from somewhere
gz := gzip.NewWriter(w)
json.NewEncoder(gz).Encode(&myStruct)
gz.Close()
}