我的需求是,输入 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 了
101
ly020044 153 天前
看一下 ```json.Marshaler``` 这个接口
|
102
ly020044 153 天前
看一下 json.Marshaler 这个接口
|
103
hekkowoerld 153 天前 1
与其说是其他人在这攻击楼主不会用,倒是楼主积极探索的态度值得鼓励,本人也觉得这个用法很蹩脚。
|
104
jc89898 153 天前
啥也不是
|
105
lesismal 153 天前
@zzhaolei
@XyIsMy 一是: orm 本身是二次加工生成代码,不是直接 sql 语句不直观。日志 level 的方式打印出来的在复杂 orm 使用过程中、可能随着输入参数不一样可能做不到线上所有情况的 sql 范式覆盖,而且 orm 还有一些隐式的东西不是程序员自己设定的,比如一些 orm 库处理数据库的 null 值还是什么来着, 需要额外去绕过,具体哪个库哪些坑我记不清了,各位可以自己搜索引擎搜下或者官方 issue list 去看,我因为抵制 orm 所以对 orm 也没深入研究 二是,这也是更重要的一点,orm 培养了很多程序员的使用习惯、抛弃了 raw sql ,我见过的绝大多数初级中级同行、以及多数所谓的高级开发(至少八年十年以上经验),都是只要 orm 能实现功能就搞上去了,性能神马的都是事后线上出了问题再去搞。比如上面说的日志 level 打印出 sql 来, 但很多人习惯了 orm 开发方式这种思维,即使打印出来也不会去关注 sql 性能相关. 咱国内人口基数大, 中等规模的公司就可能经常遇到数据量较大的量级, 在做这类项目的时候、尤其是 OLAP 涉及大量统计汇总操作的,一个没留神就被 orm 选手搞性能事故了. 跟这类兄弟共事, 为了避免这些, 研发流程上已经做了很多把关, 但仍瑟瑟发抖. |
106
lesismal 153 天前
@zzhaolei #93 不是回复我的,我之前没看到,可以看下我 #105 的,也可以看些其他帖子,比如 Xargin 的:
https://github.com/cch123/blog_comment/issues/254 还有一些其他帖子例如: https://juejin.cn/post/7174222105788514334 https://studygolang.com/articles/23459 你可以看到一些 orm 、非 orm 优劣的对比包括 gorm 或者 xorm 的一些坑你都可以去搜下看看。单就性能风险这一点上,高阶一点的开发者基本上是达成一致的,orm 自己的泛型导致的性能损失都是小损失,最大的风险就是我在#105 说的,或者 Xargin 文章里讲的。只有没怎么经手过大项目的才会对 orm 这个坑人风险的说法这么不屑一顾 “我就是好奇,这些人张嘴闭嘴就是 ORM 有不确定性,到底有什么不确定性,自己测试过?还是踩过坑?还是说嘴一张一闭反正我不管,ORM 就是有性能问题?” 这个观点是你在自己现阶段水平下得出的结论,但当你以后技术进阶了很可能会自己推翻自己这个观点。如果对技术有追求,可以多试试大项目的工作。如果现在的工作项目量级对性能没那么大要求且钱多那就无所谓了,能挣钱就行,技术都是浮云 |
107
ryalu 153 天前
@XyIsMy #91 上家公司的头头与他的思想恰恰相反,因为团队里难免技术参差不齐,很难避免有人写出垃圾 sql 导致 sql 注入的风险,反而会鼓励我们用 orm..... 上手简单,很难写出危险 sql
|
108
XyIsMy 153 天前
@lesismal 风险和习惯这个确实是存在的,避免不了。那怕定好流程规范,开发人员也一样会产出风险。这层只能靠不断的灌输思想和从架构上避免风险。
但也理解你的想法了,想尽可能使用原生 sql ,一个是降低 orm 隐性的 bug (不一定有,但想尽可能避免),另外一层,在出现性能问题时,可以快速定位问题( orm 在定位 sql 这层是办不到的) |
109
pangdundun996 153 天前
在 python 中,官方库就可以返回[{'col1': 1, 'col2': '2', 'col3': true}....] 这种带类型的 json 结果。
------ 谁能解释一下啥叫带类型的 json 吗? |
110
XyIsMy 153 天前
@ryalu 哈哈哈哈, 这个没有绝对的对错, 每个团队的习惯和方案都是经过很多业务问题沉淀出来的。下面简单的举例一下,并不严谨和也不全面。
比如 1.小型业务,团队头头会更关注,csrf ,xss ,sql 注入等等攻击风险,因为还跑不到数据库性能层 2.中型业务,团队头头会更关注,csrf ,xss ,sql 注入 + 应用 sql 调优 3.大型业务,团队头头会更关注,csrf ,xss ,sql 注入 + 应用 sql 调优 + 数据库实例调优 ,数据安全 等等 |
111
qW7bo2FbzbC0 OP 另外 sqlx 最新版本也修复了 map[string]interface{}返回 base64 encode 字符的问题
|
112
ukissme 153 天前
其实还好,楼主少见多怪,自己菜
|
113
lambdaq 153 天前
|
114
qW7bo2FbzbC0 OP @all mysql driver 还有这个问题,int 正常,但是 string 还是 base64 encode 字符串,json.RawMessage 也不顶用。sqlite3 的驱动是正常的
|
115
qW7bo2FbzbC0 OP @lambdaq 和强弱类型没有关系,驱动底层逻辑问题
|
116
lambdaq 153 天前
@qW7bo2FbzbC0 如果说关系的话,你举的这个例子,属于强类型语言里禁术。一个 list/array 里最好类型都是整整齐齐统一的。也就是那个帖子里所说的 “现在好了,到处都是字典 + 字符串取值,这个字符串 key 还是尼玛自己定义的,和 idl 都对不上,得自己去代码里面到处翻找,看看到底是哪个接口返回的。”
所以我觉得你们俩要不打一架。 |
117
baolongqishi 153 天前
|
118
zmqiang 152 天前
这个问题遇到过,这个肯定和 go 的 json 库无关,最多怪到数据库上的 driver 上,没有做好数据类型的映射
|
119
debugman66 152 天前
@XyIsMy 有点好奇是什么样的大公司,连个慢 sql 监控的工具都没有,还需要手动去看代码定位
|
120
XyIsMy 152 天前
@debugman66 我也没表达大公司需要手动定位慢 sql 的意思啊。这个说法哪来的哦
|
121
mark2025 151 天前 1
@lesismal 普通 CRUD 项目,orm 是顺手,不过也不比手写 sql 快多少。 多表关联业务系统,orm 就是灾难:光是定义实体类就能让你建吐……
|
122
mark2025 151 天前
@pangdundun996 我也想知道啥是“带类型的 json”,难道说的的是 shema 还是 类似 ts 的 interface ?
|
123
a132811 150 天前
楼主已经自己确认了答案了:是这个 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 |
124
lesismal 149 天前
@mark2025 其实我觉得还是 raw sql 简单点,orm 自己就一大套东西要学,然后不同语言不同 orm 框架都可能存在不同的表达、不同的隐式、不同的坑,但 raw sql 同一个数据库通吃,而且 sql 本身的表达能力远远比 orm 优秀。
|
125
mark2025 149 天前
@lesismal 我觉得 SQL 是一个非常优秀的编程语言,要把表达明晰的 SQL 转换成另外一个表达方式(比如 ORM )是很困难的。
CRUD 以及简单连表操作我用 kenx 的 querybuilder ,复杂查询直接写 raw sql 去执行。 |
126
lesismal 149 天前
@mark2025 对呀, 所以我一直想不明白很多人号称 orm 比 sql 简单是为什么, 因为我一直学不会 orm. 不是看不懂 orm, 而是因为抵制 orm, 所以每次都不想学, 所以学不会. 要避坑 orm 先要学好多东西, 这本身并不比 raw sql 容易
|
127
mark2025 149 天前 1
@lesismal SQL 是带有过程式风格的语言,要和面向对象的 ORM 之间转换,先不说结果如何,遇上复杂连表查询场景开发人员脑细胞就得 s 一大堆来构造这个对象的调用。
公司一个公积金项目,java 同事先是用 mybatis 来弄,等实体类创建了一百个之后就绝望放弃了。最后改成 hibernate 那种 xml 方式…… xml…… 可 xml 没发做 ide 的自动提示完成啊。结果某个接口有 130 个字段,有次的 bug 就是字段名写错了…… |
128
mocococ 148 天前
其实可以用 其他比较优秀的框架来试试, 比如 goframe, 这个框架的 orm,绝对够用
|
129
qW7bo2FbzbC0 OP |
130
mark2025 134 天前
@ryalu 不同阶段问题矛盾不同
- 普通 CRUD 场景/中小规模阶段,orm 可以避免新手菜鸟挖坑 - 复杂业务统计场景/大规模阶段,任何小失误都可能会放大成大事故,所以 orm 虽然一方面可以避免菜鸟的低级错误,但另一方面对老鸟也能反向挖坑,所以需要 DBA 来把关(审查 SQL ) |
131
Brau1589 56 天前
你可以 自定义个结构体然后重写一下 Scan 方法
func (你的结构体) Scan(value interface{}) error |