
本文介绍在 Go 语言中精确获取 json.Encoder 输出数据大小的两种方法,重点推荐基于 io.Writer 嵌入的计数器方案,兼顾准确性、性能与可复用性,并提供完整示例与关键注意事项。
本文介绍在 go 语言中精确获取 `json.encoder` 输出数据大小的两种方法,重点推荐基于 `io.writer` 嵌入的计数器方案,兼顾准确性、性能与可复用性,并提供完整示例与关键注意事项。
在 Go 中使用 json.Encoder 进行流式 JSON 编码时,标准库并未暴露已写入字节数——json.Encoder 本身不跟踪写入量,其底层 io.Writer 接口也未强制要求实现 Write 的计数能力。因此,若需获知编码结果的精确大小(单位:字节或比特),需主动介入写入过程。
方案一:先 Marshal 再计算(简单但有开销)
最直观的方式是调用 json.Marshal() 将对象序列化为 []byte,再用 len() 获取字节数,乘以 8 即得比特数:
data, err := json.Marshal(s)
if err != nil {
panic(err)
}
bytesLen := len(data) // 字节数
bitsLen := bytesLen * 8 // 比特数
// 然后手动写入目标 io.Writer(如 buf.Write(data))
✅ 优点:逻辑清晰、无需额外类型。
❌ 缺点:对大对象会产生冗余内存拷贝(先生成完整字节切片,再写入),且破坏了 Encoder 的流式优势。
方案二:自定义计数 Writer(推荐,高效且优雅)
通过结构体嵌入 io.Writer 并重写 Write 方法,可无缝集成计数逻辑,零侵入适配任意 json.Encoder:
type CounterWriter struct {
io.Writer
Count int
}
func (cw *CounterWriter) Write(p []byte) (n int, err error) {
n, err = cw.Writer.Write(p)
cw.Count += n
return
}
该类型将写入字节数自动累加至 Count 字段,使用方式简洁:
buf := &bytes.Buffer{}
cw := &CounterWriter{Writer: buf}
s := struct{ S string; I int }{"hello", 4}
err := json.NewEncoder(cw).Encode(s)
if err != nil {
panic(err)
}
fmt.Printf("Size: %d bytes (%d bits)\n", cw.Count, cw.Count*8)
// 输出:Size: 20 bytes (160 bits)
关键注意事项
- 累计计数:cw.Count 是自创建以来所有写入的总字节数。若需单次编码大小,应在调用 Encode() 前重置:cw.Count = 0。
- 线程安全:上述 CounterWriter 非并发安全;多 goroutine 共享同一实例时,需配合 sync.Mutex 或改用 atomic.AddInt64。
- 验证一致性:可同步比对底层 bytes.Buffer.Len(),二者值应严格相等,用于调试校验。
- 通用性:该模式适用于任何基于 io.Writer 的编码器(如 xml.Encoder, 自定义协议序列化器),具备高度复用价值。
综上,优先采用嵌入式 CounterWriter 方案——它既保留了 json.Encoder 的流式性能优势,又以最小侵入代价提供了精准的尺寸监控能力,是生产环境中的最佳实践选择。











