本文介绍两种在 Go 中精确获取 JSON 编码结果大小的方法:一种是通过 json.Marshal 预计算字节数,另一种是使用嵌入式计数写入器(CounterWr)实时统计 json.Encoder 输出的字节数,并换算为比特数。
本文介绍两种在 go 中精确获取 json 编码结果大小的方法:一种是通过 `json.marshal` 预计算字节数,另一种是使用嵌入式计数写入器(`counterwr`)实时统计 `json.encoder` 输出的字节数,并换算为比特数。
在 Go 开发中,常需评估序列化开销(如网络传输、存储或性能调优),而 json.Encoder 本身不提供已写入字节数的反馈。直接调用 json.Marshal 虽可快速获取字节长度(len([]byte)),但存在内存冗余和额外拷贝问题——尤其对大型结构体,先全量序列化再写入,违背流式编码的设计初衷。
更优雅、高效的方案是扩展 io.Writer 行为,通过结构体嵌入实现写入计数。以下是一个轻量、线程安全(单 goroutine 场景下)、符合 Go 接口习惯的计数写入器:
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
}
该类型通过嵌入 io.Writer 实现接口兼容性,并重写 Write 方法,在每次写入时累加实际字节数。它无需缓冲、不改变原有流程,且与任意 io.Writer(如 bytes.Buffer、os.File 或 net.Conn)无缝集成。
使用示例如下:
type Something struct {
S string
I int
}
buf := &bytes.Buffer{}
cw := &CounterWr{Writer: buf}
s := Something{"hello", 4}
err := json.NewEncoder(cw).Encode(s)
if err != nil {
panic(err)
}
fmt.Printf("Encoded size: %d bytes (%d bits)\n", cw.Count, cw.Count*8)
// 输出:Encoded size: 20 bytes (160 bits)
✅ 关键优势:
- 零内存复制,保持 json.Encoder 的流式特性;
- 计数精确(基于底层 Write 返回值,而非字符串长度或 UTF-8 字符数);
- 可复用:同一 CounterWr 实例可连续编码多个对象,Count 累计总大小;
⚠️ 注意事项:
- 若需单独获取某次 Encode() 的字节数,请在调用前记录 cw.Count,编码后再相减;或直接重置 cw.Count = 0;
- CounterWr 本身非并发安全,多 goroutine 同时写入需配合 sync.Mutex 或改用原子操作(如 atomic.AddInt64);
- 比特数严格等于 字节数 × 8(Go 中 byte 恒为 8 位),无需考虑编码字符集或 Unicode 变长问题——JSON 序列化结果始终是 UTF-8 字节流。
综上,CounterWr 方案兼顾简洁性、性能与可维护性,是生产环境中测量 JSON 编码体积的推荐实践。











