Gjson不适合超大数据通用解析,仅按路径捞值、不校验不反序列化;处理GB级JSON必须用json.Decoder流式读取,否则内存必爆;调用gjson.GetBytes前须检查ContentLength并设硬上限(如2MB),否则BOM或超长payload会导致panic。

Gjson 不适合做超大数据的“通用解析器”,它只负责从原始 []byte 里按路径捞值,不建结构、不校验、不反序列化;真要处理超大 JSON(比如 GB 级日志或批量响应),必须搭配 json.Decoder 流式读取,否则内存必爆。
gjson.GetBytes() 在中间件里怎么防 panic 和内存暴涨
HTTP 请求体若含 BOM、多余空白或超长 payload,gjson.GetBytes() 会直接 panic——它不跳过非法前缀,也不限长。而 json.Unmarshal 至少能跳过空白并报错,不会崩。
- 必须在调用前检查
r.ContentLength,设硬上限(如2 即 2MB),否则恶意请求可触发 OOM - 别反复对同一份 body 调
gjson.GetBytes():每次都是全量扫描,路径匹配仍是线性时间,开销白费 - 路径字符串禁止拼接(如
"data." + field),gjson不过滤..或[0],有注入风险;硬编码路径最安全 - 提取后务必用
result.Exists()和result.IsString()双重判断,.String()静默返回空串,.Int()静默返回 0
为什么 decoder.Decode(&[]T) 依然会 OOM
很多人误以为 json.NewDecoder(r.Body).Decode(&[]YourStruct{}) 是流式,其实不是:它仍会一次性分配整个 slice 并填满所有元素。100 万条记录 = malloc 100 万个 struct 实例 + slice 扩容冗余空间,哪怕你只取前 10 条。
-
decoder.Decode(&[]T)和json.Unmarshal内存行为本质相同,只是输入源不同 - 正确做法是手动控制 token:先
dec.Token()确认开头是'[',再进for dec.More() { dec.Decode(&item) }循环 - 循环体开头加
if !dec.More() { break },否则dec.Decode()在]后多读一次,报invalid character '}' after top-level value - 不确定结构时,用
json.RawMessage接整块字节,后续再用gjson.GetBytes()提字段,避免全量解析
gjson + json.Decoder 混合使用的典型场景
纯流式写跳过逻辑太重,纯 gjson 又拿不到 struct;二者互补:gjson 快速定位,Decoder 精准解码子片段。
立即学习“go语言免费学习笔记(深入)”;
- 例如处理
data.items.#.name:先用gjson.ParseReader(r.Body)扫一遍,拿到所有name字段的 byte offset 和长度 - 再用
io.MultiReader或bytes.NewReader构造子区间,传给json.NewDecoder()解析单个name对应的对象 -
gjson不修改原数据,只做只读索引;json.Decoder负责真正反序列化——分工明确 - 注意:gjson 解析后不能复用 body,必须提前
io.ReadAll缓存,或用bytes.Buffer复制一份
真正卡住性能的从来不是解析库本身,而是你让 gjson 去干它不该干的事:比如校验结构、反序列化嵌套对象、处理带 BOM 的 HTTP body。它快,是因为它极简;它危险,也是因为太简。用对地方,它就是最快的探针;用错场景,它就是隐蔽的 panic 源头。



















