被 go 语言的 json.Marshal 恶心到了

201 天前
 qW7bo2FbzbC0

我的需求是,输入 sql ,返回序列化后的 json 结果。

在 python 中,官方库就可以返回[{'col1': 1, 'col2': '2', 'col3': true}....] 这种带类型的 json 结果。

在 go 中如果已知 sql 返回的列数和列类型,也可以构造一个 struct 进行数据映射,然后用json.Marshal转为 json 。

如果 sql 返回的列数和行类型未知,就很难受了,在 go/mysql 的官方 wiki 案例 中对于匿名的结果,使用了 interface 或者 sql.RawBytes ,但这两种替代方式在json.Marshal后都变成了 base64 encode 后的 string ,既丢失了类型也变异了结果( https://stackoverflow.com/questions/32501784/the-sqlx-library-gives-weird-base64-encoded-looking-results)

请问各位在实际业务中遇到这个问题是怎么处理的?

在其他语言中很自然的 object 序列化为原类型,在 go 的 json.Marshal 中怎么就全变成 string 了

13267 次点击
所在节点    Go 编程语言
131 条回复
mark2025
198 天前
@lesismal 普通 CRUD 项目,orm 是顺手,不过也不比手写 sql 快多少。 多表关联业务系统,orm 就是灾难:光是定义实体类就能让你建吐……
mark2025
198 天前
@pangdundun996 我也想知道啥是“带类型的 json”,难道说的的是 shema 还是 类似 ts 的 interface ?
a132811
197 天前
楼主已经自己确认了答案了:是这个 sql 库本身实现得有问题,跟 go 、json.Marshal 都无关。

在某种意义上还是 json 的表达能力不足导致的

## 不同语言的 json 库对[]byte 的处理
json  本身无法区分 string 和 `[]byte`,只有 string 。

1. php 自己本身就没有`[]byte`,甚至连 dict/array 都不区分。直接回避了问题
2. python  有`[]byte`, 但是不能 json 序列化, 如果是 utf8 就需要用 data.decode('utf-8') 要先转成 str
3. golang  序列化`[]byte` 时用 base64 将它转成 string, json 反序列时则用相应的类型去关联`[]byte`类型
4. java , c sharp 跟 golang 处理方式一样的。

## golang 对 json 的支持

json.Marshal 方法:

1. json.Marshal 本身是使用了内置的 json encoder 默认使用 base64 处理 bytes, eacapHTML 默认为 true
2. 但是 json.RawMessage 本身带的 json encoder ,不会对 bytes 做任何处理,如果 bytes 不是 json 格式,就会报编码出错

某些 golang  库实现,可能出于减少值复制,会用 bytes 去表达 string ,但序列化时就只能是 base64   string 了。

如果接收者不要 base64 string json, 解决办法有:

1. 自己先定义一个相应 string struct ,再将 bytes struct 转成 string struct ,最后 json  序列化自然就不是 base64 了
2. 通过反射将 string struct/map 等 转成  string map 再 json. 参考: https://github.com/ahuigo/golib/tree/main/spec/object/convert/objbytes2string_test.go
lesismal
196 天前
@mark2025 其实我觉得还是 raw sql 简单点,orm 自己就一大套东西要学,然后不同语言不同 orm 框架都可能存在不同的表达、不同的隐式、不同的坑,但 raw sql 同一个数据库通吃,而且 sql 本身的表达能力远远比 orm 优秀。
mark2025
196 天前
@lesismal 我觉得 SQL 是一个非常优秀的编程语言,要把表达明晰的 SQL 转换成另外一个表达方式(比如 ORM )是很困难的。
CRUD 以及简单连表操作我用 kenx 的 querybuilder ,复杂查询直接写 raw sql 去执行。
lesismal
196 天前
@mark2025 对呀, 所以我一直想不明白很多人号称 orm 比 sql 简单是为什么, 因为我一直学不会 orm. 不是看不懂 orm, 而是因为抵制 orm, 所以每次都不想学, 所以学不会. 要避坑 orm 先要学好多东西, 这本身并不比 raw sql 容易
mark2025
196 天前
@lesismal SQL 是带有过程式风格的语言,要和面向对象的 ORM 之间转换,先不说结果如何,遇上复杂连表查询场景开发人员脑细胞就得 s 一大堆来构造这个对象的调用。
公司一个公积金项目,java 同事先是用 mybatis 来弄,等实体类创建了一百个之后就绝望放弃了。最后改成 hibernate 那种 xml 方式…… xml…… 可 xml 没发做 ide 的自动提示完成啊。结果某个接口有 130 个字段,有次的 bug 就是字段名写错了……
mocococ
195 天前
其实可以用 其他比较优秀的框架来试试, 比如 goframe, 这个框架的 orm,绝对够用
qW7bo2FbzbC0
194 天前
mark2025
181 天前
@ryalu 不同阶段问题矛盾不同
- 普通 CRUD 场景/中小规模阶段,orm 可以避免新手菜鸟挖坑
- 复杂业务统计场景/大规模阶段,任何小失误都可能会放大成大事故,所以 orm 虽然一方面可以避免菜鸟的低级错误,但另一方面对老鸟也能反向挖坑,所以需要 DBA 来把关(审查 SQL )
Brau1589
104 天前
你可以 自定义个结构体然后重写一下 Scan 方法
func (你的结构体) Scan(value interface{}) error

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

https://tanronggui.xyz/t/1057942

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

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

© 2021 V2EX