json.Unmarshal可解析文件数据,json.NewDecoder.Decode为何失败?
两种JSON反序列化方式报错的原因分析
现象对比
- 以下代码可正常完成结构体反序列化:
func foo() { d, err := os.ReadFile("file.json") var t T if err := json.Unmarshal(d, &t); err != nil { panic(err) } }
- 但以下代码会抛出EOF、
unexpected token 't'等JSON解析错误:
func foo() { f, err := os.Open("file.json") var t T if err := json.NewDecoder(f).Decode(&t); err != nil { panic(err) } }
核心原因(结合多goroutine场景)
问题出在两种方式对数据源的处理特性差异,以及你提到的「os.File或[]byte被同时用于两个goroutine」的场景:
os.ReadFile是一次性将整个文件内容读取到内存的[]byte切片中,后续就算多个goroutine共享这个切片,只要不对切片做修改,每个goroutine拿到的都是完整、独立的JSON内容副本,json.Unmarshal可以基于完整数据正常解析。os.Open返回的os.File是一个带读取偏移量的有状态句柄,文件的读取位置会随着每次读操作自动移动。如果多个goroutine同时使用同一个os.File实例(比如各自创建Decoder去Decode),会导致文件指针的偏移被多goroutine的读操作打乱:A goroutine读了一部分内容后指针移到中间,B goroutine接着读时只能拿到从中间开始的截断JSON片段,自然会触发EOF、非法token这类解析错误。
补充:你提供的JSON结构(包含data数组的对象)本身没有语法问题(第一种方式能正常解析也佐证了这一点),所以报错和JSON结构无关,完全是多goroutine共享有状态文件句柄导致的。
内容的提问来源于stack exchange,提问作者jclasley
相关产品推荐
相关产品推荐

