踩到 Go 的 json 解析坑了,如何才能严格解析 json?

2023-09-19 15:28:01 +08:00
 BeautifulSoap

精准踩中了 json 解析包的两个坑导致了生产环境出错

假设有下面结构体定义

type Data struct {
	A   string `json:"a"`
	B   int   `json:"b`
	Obj struct {
		AA string `json:"aa"`
		BB int    `json:"bb"`
	} `json:"obj"`
}

使用json.Unmarshal() 解析下列几种 json

{"a":null, "b": null, "obj":null}
{"obj": null}
{"a": "a"}
{"a": "a","z":"z"}
{}
{"obj": {}}

问:解析哪个 json 会报错?

答:全都不报错都正确解析

都是不出事就注意不到的问题。尤其非指针类型字段,我下意识认为遇到 null 是会直接报错的,结果直接是当作不存在(undefined)来处理。。。

so ,go 下怎么才能简单地进行严格 json 解析?要求

  1. 不允许出现未知字段,出现则报错(这个似乎倒是可以用 json 包的 DisallowUnknownFields 简单做到)
  2. 非指针字段不允许传入 null ,否则报错(似乎 json 包没法简单做到)
15402 次点击
所在节点    Go 编程语言
211 条回复
menuvb
2023-09-20 14:31:30 +08:00
可以考虑第三方的解析库提取数据并做 nil 判断,避免执行 Mashal/Unmashal 操作。https://github.com/antchfx/jsonquery
BeautifulSoap
2023-09-20 14:32:00 +08:00
@lolizeppelin 你别说,在踩到 null 这个坑之前可能真的挺多人都注意不到这个问题。而且你说的怎么感觉好反思怪啊。学语言总有没有注意到或记住的细节,出问题或者被人指出来前是很难发现的。约定好非 null 但传了 null ,属于不太常发生的事情,所以才很久没出问题。但墨菲定律就是这么发生了,出了事故了

@pkoukk 你做人怎么能这么双标呢?自己提出所有字段定义成指针,然后被我指出倒腾指针反人类后就提出让我再多写一层 struct 转换过去,在业务层倒腾 struct 。结果我的项目就是这么做的却反过头来说这是臃肿抽象? ps:我不会写 java 谢谢,我和 java 关系最近的也就是曾经学过一小会 kotlin ,仅此而已。项目分层无关语言是几乎所有复杂点的业务写到后来都会自然而然分的。我很喜欢 Go ,但对于像你这样动不动就让人在自己身上找问题,攻击其他语言的 Go 的小部分拥趸,真的很令人不适
eijnix
2023-09-20 14:37:57 +08:00
用 protobuf
powerless
2023-09-20 14:40:06 +08:00
@Jammar 花西子牌程序员
pkoukk
2023-09-20 14:54:28 +08:00
@BeautifulSoap #142 如果你在写 go 之前,认真的看过这个东西,你就会明白我说的是什么意思 https://google.github.io/styleguide/go/guide
不想和你继续下去了,你不是来寻找解决方案的,你只是来吐槽的,给你说再多你都认为别人在攻击你,实在是没必要和你继续浪费时间了
yanue
2023-09-20 14:58:57 +08:00
要通过值判断,而不是判断 null
BeautifulSoap
2023-09-20 15:22:24 +08:00
@pkoukk so ,这和你搞双标有哪怕任何一点关系吗?
ps:解决办法 21L 就说了已经找到方向了。json 包魔改了一下加了一句判断工作良好已经用到项目里去了。这贴之所拉这么长,是真的太多人的回复(包括你)过于离谱了。就比如这个帖子里所有说这 null 解析是 validation 问题的,明显就试都没试过就一副很懂得样子教育人是 validation ( json 包这个特性实际上根本就没法做 validation )。再比指针,我 2L 就说过为什么不现实了,你硬是要教育人强行说它现实一样
o562dsRcFqYl375i
2023-09-20 15:22:36 +08:00
@Jammar 有没有好好加班
tiedan
2023-09-20 15:29:51 +08:00
UnmarshalJSON 或者指针,指针其实没啥大问题,稍微有一些嵌套的结构体,指针就是挺多的。。
chor02
2023-09-20 15:38:16 +08:00
这是 go 基础类型特性,使用 mapstructure 做一次校验吧
lujiaxing
2023-09-20 15:50:46 +08:00
@BeautifulSoap 那我就要问问了, 测试干嘛去了. 上线前不做集成测试的么? 怎么可能出现两边接口对不上的问题?

而且如果你要是真对自己团队的成员都如此不信任的话, 那我就只能建议你放弃 json, 改用更加严格的 SOAP 或者 GRPC 了. 那玩意不用校验, 说好的字段缺一个字都会炸.
lujiaxing
2023-09-20 15:55:29 +08:00
@BeautifulSoap 除非你们其中某一个微服务子系统是用弱类型语言开发的 (Javascript), 否则不可能出现 "某一种情况下有这个字段另一种情况下没有这种字段" 的离谱情况. 如果缺字段, 应该是在测试阶段就已经发现了. 毕竟缺字段会导致整个程序跑不通. 我不明白你们公司的业务是怎么搭建的, 怎么测试的, 怎么可能出现其中一个微服务没有按照约定返回字段却顺利通过了的 codereview, ut, 集成测试的.....
lujiaxing
2023-09-20 15:57:12 +08:00
@lujiaxing 再不就是系统里不当使用了 dynamic 类型
ttvast
2023-09-20 16:16:41 +08:00
什么叫“默认空值”, 默认值的意思就是你不输入,我就设置成这个值。你给的是 null ,就表示没有输入,所以就是调用默认值。

对于一个有默认空值的整数字段来说,null 和 0 就是一回事情,设计业务的时候就应该这样定义。
codersdp1
2023-09-20 16:19:04 +08:00
竟然声明了 price 为 int ,如果传了{price:null},只有两种结果,1. UnmarshalJSON 不报错,price 为初始值,2.报错
go 标准库用了第一种 doc 也给出了提示,如果想得到第二种效果得改下标准库满足你的需求,或者定义成*int 。
BeautifulSoap
2023-09-20 16:22:58 +08:00
@lujiaxing 别逼逼
1. "请问你能教教我 json 这种 null 解析逻辑我该怎么做合法性校验呢" 静听高论,希望你能提出切实有效的方案而不是在这纸上谈兵。这个问题我自己已经解决了,只是像听听你会有什么更好的解决办法
2. 冷知识,实际项目中,外部接口可不光团队内部接口,还可能包括很多质量参差不齐对接的第三方非本公司服务的接口哦
3. 你是这个帖子第三个还是第四个动不动就问”测试干嘛去了”的人了。现实世界中测试不可能覆盖掉所有情况,总有纰漏,即便花费大心思去思考各种边界条件总归会出纰漏,对于复杂业务你想破脑袋现实都会给你一巴掌。而一有纰漏就质问“测试干嘛去了”的人,我觉得嘴脸十分丑恶
4. 我只问你一个问题你同不同意“后端不应相信前端/外部接口传来的数据”这一准则。如果你同意,无论你的团队编程质量多高,你就应该以最坏情况去写代码,你死鸭子嘴硬说永远不可能传 null 不校验那就是你自己都不遵守这一准则,把安全性的一切交给外部接口
zmcity
2023-09-20 16:31:04 +08:00
golang 就是这样的,如果你还不想写指针,就 map[string]interface{}一把梭好了。
或者直接 grpc ?
rekulas
2023-09-20 16:48:37 +08:00
@Nugine0 是的我的失误 rust 官方库是支持校验的
Gota
2023-09-20 16:49:00 +08:00
像这种对请求格式要求比较严格的场景,还是建议找个语言无关的 RPC 框架做通信和校验。不然就算一个服务里处理好了,不同语言服务间调用,或者服务质量参差,还是可能遇到类似的问题。
gogogo1203
2023-09-20 17:01:40 +08:00
@pkoukk 因为一开始就吐槽错了,为了掩盖自己的错, 一直持续对线. 标题误导人, 他只是为了吐槽而已.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/975214

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX