-1

I have an object. I encode the object to json using json.Encoder.

How can I measure the size of the json string in either bits?

icza
  • 389,944
  • 63
  • 907
  • 827
user1283776
  • 19,640
  • 49
  • 136
  • 276
  • What have you tried? You use the builtin `len` to get the length of strings, slices, maps, and buffered channels. – JimB Jun 08 '16 at 12:56
  • objectInJson, err := json.Marshal(object); len(objectInJson) works well. But I would prefer to not use json.Marshal as I use json.Encode for another purpose. I would like to get the string length from json.Encode somehow – user1283776 Jun 08 '16 at 12:58
  • If you're using a `json.Encoder`, what are you writing to? Can you just write to a buffer and check its length after? – JimB Jun 08 '16 at 13:03

1 Answers1

3

io.Writer and json.Encoder does not expose nor maintain number of written bytes.

One way would be to first marshal the value using json.Marshal() into a []byte whose length we can get with the builtin len() function. The bit count you seek for is the length multiplied by 8 (1 byte is 8 bits). After that you have to manually write the byte slice to your output. For small types, this is not a problem, but it may be undesirable for large structs / values. Also there is unnecessary work marshalling it, getting its length and writing the slice manually.

A much better and more elegant way is to extend the functionality of any writers to manage the written bytes, using embedding:

type CounterWr struct {
    io.Writer
    Count int
}

func (cw *CounterWr) Write(p []byte) (n int, err error) {
    n, err = cw.Writer.Write(p)
    cw.Count += n
    return
}

This CounterWr type automatically manages the number of written bytes in its Count field which you can check / examine at any time.

Now you create a value of our CounterWr passing the io.Writer that you currently use, and then pass this CounterWr value to json.NewEncoder(), and you can access number of written bytes from CounterWr.Count directly.

Example usage:

type Something struct {
    S string
    I int
}


buf := &bytes.Buffer{}

// Any writer, not just a buffer!
var out io.Writer = buf
cw := &CounterWr{Writer: out}

s := Something{"hello", 4}
if err := json.NewEncoder(cw).Encode(s); err != nil {
    panic(err)
}

fmt.Printf("Count: %d bytes, %d bits\n", cw.Count, cw.Count*8)
fmt.Printf("Verif: %d bytes, %d bits\n", buf.Len(), buf.Len()*8)

For verification purposes we're also printing the length of the bytes.Buffer we used as our output (CounterWr.Count and Buffer.Len() should match).

Output:

Count: 20 bytes, 160 bits
Verif: 20 bytes, 160 bits

Try it on the Go Playground.

Notes:

If you encode other values too, cw.Count will be the number of total bytes of course (and not just that of the last value). If you want to get the size of the last encoded value only, store cw.Count before calling Encoder.Encode(), and calculate the difference to the count you get after encoding it. Or simply set cw.Count to 0 before encoding (yes, you can also change that field):

cw.Count = 0
icza
  • 389,944
  • 63
  • 907
  • 827