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

200 天前
 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 了

13258 次点击
所在节点    Go 编程语言
131 条回复
veni2023
200 天前
@pkoukk 遇到过这个包在弱类型解析时有精度丢失的情况
houzhiqiang
200 天前
我猜想要的效果是这样的:
```python
import pymysql
import pymysql.cursors


connect = pymysql.connect(
host="example.com", port=3306, user="user", password="password", database="database_name",
cursorclass=pymysql.cursors.DictCursor,
)
with connect.cursor() as cursor:
cursor.execute(
"""select * from users limit %(param_limit)s""",
args={"param_limit": 2}
)
results = cursor.fetchall()
print(results)
# [
# {'id': 13, 'birthday': datetime.date(1900, 1, 1), 'create_time': datetime.datetime(2020, 9, 3, 16, 56, 39)},
# {'id': 39, 'birthday': datetime.date(1900, 1, 1), 'create_time': datetime.datetime(2020, 9, 14, 17, 5, 1)}
# ]
```
vczyh
200 天前
@qW7bo2FbzbC0

func TestMy(t *testing.T) {
db, err := sql.Open("mysql", "root:123@/test")
if err != nil {
t.Fatal(err)
}
defer db.Close()

rows, err := db.Query("SELECT id FROM t1")
if err != nil {
t.Fatal(err)
}

for rows.Next() {
var id any
if err := rows.Scan(&id); err != nil {
t.Fatal(err)
}
m := map[string]any{
"value": id,
}
b, err := json.Marshal(m)
if err != nil {
t.Fatal(err)
}
t.Log(string(b))
}
if err := rows.Err(); err != nil {
t.Fatal(err)
}
}

{"value":1}


用 any 接收数据,返回的类型由驱动实现决定,database/sql 只提供接口,驱动负责返回 sqldriver.Value ,所以真不是 Go 语言的问题。
jlkm2010
200 天前
go 的语法很怪异
kkbblzq
200 天前
这跟 json 都没有关系,而是 sql 类型和 go 类型之间的转换问题。
qW7bo2FbzbC0
200 天前
@vczyh 你这个是什么驱动?
vczyh
200 天前
vczyh
200 天前
lesismal
200 天前
@james122333
1. orm 是非常不好的选择, 中小项目倒是还可以, 大项目大数据量的, 用 orm 存在一些不确定性可能导致性能风险, 所以我都是禁止团队试用 golang orm 的
2. 还有生成代码的 sqlc 这种, 性能当然是最友好但用起来也有点麻烦, 因为一些业务是上下文比较复杂的, 单纯生成一段 sql 对应的 go 代码还需要 ctrl cv 而且还要修改对应的地方, 功能修改的时候比较麻烦
3. sqlx 简化了一些 binding, 但也把一些简单的复杂化了, 比如又有 Select 又有 Get, 比如 Insert 缺少 struct 的自动绑定(我没深入使用只是看它文档和例子), 综合下来我觉得它少了一些可以真正节约体力的但多了不少没必要的, 所以对我来说病不好用

所以以前还是继续标准库 raw sql, 直到我实在忍不了标准库 raw sql 自己搞了这个
lifei6671
200 天前
@qW7bo2FbzbC0 #37 没看到别人扣帽子,反而是你自己情绪化,人家都说了你用不惯 go 就换成你喜欢的语言好了,再说了,interface 满天飞就是犯懒的行为。
qW7bo2FbzbC0
200 天前
@lifei6671 1.8 之前还有什么办法避免 Interface 满天飞?
lesismal
200 天前
@james122333 我这个就是简简单单几点:
1. 鼓励明确定义 table 对应的 struct, 用明确定义的 struct 操作 sql, 这个 struct 自己手写也好用工具生成也好, 都挺方便的
2. 鼓励 raw sql, 避免 orm 之类的可能生成了性能不友好的语句造成性能事故
3. 框架尽量自动映射/绑定 sql table 和 struct, insert/update/select 这些与 struct 映射绑定操作密切的都很轻松用 struct 操作
4. 奥卡姆剃刀原则, 不提供 orm, 不提供那么多没什么必要的垃圾接口
fregie
200 天前
@qW7bo2FbzbC0 "我觉得 go/sql 和 json.Marshal 对于匿名类型查询不友好也是犯懒?"
不友好的意思是指不能用方便又稳定的办法解决这个问题,只能用麻烦但是稳定的方式解决.
你认为这不友好难道不是犯懒吗?
BTW,何必这么敏感,犯懒又不是什么攻击性的词语,我自己也经常犯懒,人皆是如此,只是要可能会付出代价
这个世界还是挺美好的,没必要一进入互联网就进入攻击模式
superrichman
200 天前
@zxdstyle 你想表达的是动态语言和静态语言的区别
qW7bo2FbzbC0
200 天前
@fregie 犯懒字面上是贬义词吧,你觉得不是攻击性的,那你可以自己多读几遍,不要替别人做决定。稳定不稳定不知道,写起来麻烦的代码后面维护起来肯定更麻烦
lesismal
200 天前
@qW7bo2FbzbC0 #71
interface 满天飞是当时写这些垃圾代码的人的问题, 不是 golang 自己的问题.
正确姿势本来就应该避免 interface 满天飞, 我看到很多喜欢搞 interface 满天飞的, 是 php nodejs 之类的转 go 的人居多, 这也不能全怪他们, 毕竟以前语言习惯已经信手拈来了, 刚开始未必能意识到这样会带来工程上的问题, 更何况有时候赶工只追求先搞定业务
c/cpp 或者一些新手上路就是 go 的, 姿势正常的人多些

接手别人的屎山不是 OP 的错, interface 满天飞不是 golang 的错, OP 要怪就都怪写屎山的人吧
leonshaw
200 天前
用 ScanType 反射?
iyaozhen
200 天前
用 go 千万不要和别的语音比 不要之前语言思维定势。不然坑还更多
qW7bo2FbzbC0
200 天前
按楼上说的 json.RawMessage 解决了,我没有想到要去 json 命名空间里找类型
@leonshaw
james122333
200 天前
@qW7bo2FbzbC0

这你要问 driver 为什么输出的格式是[]byte 也就是你上面贴的那个 lib
目前没时间研究 你自立自强点
至於 json 对[]byte 处理是合理的

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

https://tanronggui.xyz/t/1057942

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

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

© 2021 V2EX