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

154 天前
qW7bo2FbzbC0  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 了

12509 次点击
所在节点   Go 编程语言  Go 编程语言
131 条回复
qW7bo2FbzbC0
qW7bo2FbzbC0
154 天前
@sagaxu 我去查了下 grafana 的代码,又查了下 sof ,对于未知返回类型的查询,的确是对各个类型自己去重新实现映射/序列化

和 @NessajCN 说的差不多

但是 java, c#这种都可以 getObject,然后直接对 Object 序列化出带类型的 json
qW7bo2FbzbC0
qW7bo2FbzbC0
154 天前
@sagaxu @NessajCN @james122333 但是 go 语言中,用 interface 或者 any 来模拟 java c#这种 getObject ,然后序列化,结果就是 base64 encode 后的 string
cuiweiqiang
cuiweiqiang
154 天前
既然用 sql 了,为啥还会存在“行类型未知”这种场景?
james122333
james122333
154 天前
是最好定义 struct 外加上泛型
qW7bo2FbzbC0
qW7bo2FbzbC0
154 天前
@dcalsky 肯定是有传入黑箱 sql ,返回 json 结果的场景,没遇到不等于没有。这和 python 、js 、php 味没关系,java c#都正常
wildlife
wildlife
154 天前
标准的 JSON 不是只能使用 "" 双引号包围的任意数量 Unicode 字符的集合吗?
qW7bo2FbzbC0
qW7bo2FbzbC0
154 天前
@cuiweiqiang 用 java 模拟 mysql 命令行工具,这个场景是不是 sql 已知,行类型未知?
james122333
james122333
154 天前
@qW7bo2FbzbC0

你不能直接就一个 any 最起码知道它是字典阵列 不知道就先 json.RawMessage 根据内容 parse 一下用字典或阵列装
xingjue
xingjue
154 天前
你适合用 PHP 典型的脚本语言思想
james122333
james122333
154 天前
阿... Marshal 的话就直接 Marshal 成 json.RawMessage 就好了
把字段定义成 json.RawMessage 即可
MoYi123
MoYi123
154 天前
@qW7bo2FbzbC0 怎么可能是 base64 后的 string 啊, 贴个例子看看. 八成是 unicode 吧, 提醒一下, json 必须是 utf8 编码的.
qW7bo2FbzbC0
qW7bo2FbzbC0
154 天前
在 go/sql 中,interface 或者 any 会返回[]byte ,在 json.Marshal 中,[]byte 会被序列化成 base64 encode 字符串


`Array and slice values encode as JSON arrays, except that []byte encodes as a base64-encoded string, and a nil slice encodes as the null JSON value.`

@NessajCN @sagaxu
qW7bo2FbzbC0
qW7bo2FbzbC0
154 天前
https://forum.golangbridge.org/t/json-encode-byte-array-as-hex-string-instead-of-base64/26751

```
One must use reflection to walk and copy the original object replacing byte arrays with the user-defined type for which the MarshalJSON method is defined, but there’s probably no better way. An even uglier alternative is to post-process the JSON.
```
fregie
fregie
154 天前
犯懒就用 js python 就好了,何必选个强类型语言给自己找罪受呢
qW7bo2FbzbC0
qW7bo2FbzbC0
154 天前
Felldeadbird
154 天前
用一门新语言时,最怕就是拿别的语言对比新学的语言。

不想麻烦就定义结构体,一次定义全代码收益,要像脚本语言那么自然,就得多写一些代码转换。
qW7bo2FbzbC0
154 天前
@fregie 1 、项目定型是这个语言。2 、类似的蹩脚问题不止这一个,在 go1.8 之前代码里面好多 inteface 飞来飞去,不仅丢失了类型还影响类型安全。 我讨厌 interface 转来转去也是犯懒,我觉得 go/sql 和 json.Marshal 对于匿名类型查询不友好也是犯懒?你扣帽子是什么态度?
cs8425
154 天前
@qW7bo2FbzbC0 因为 json 的 string 必须为 UTF-8 而[]byte 不一定是 UTF-8
Jinnrry
154 天前
你可以试试 map any any

不过建议你写 php
qW7bo2FbzbC0
154 天前
同样是使用匿名类型去序列化,c#正确识别出了 Number 类型

```c#
// See https://aka.ms/new-console-template for more information
using System.Text.Json;
using MySqlConnector;


using var connection = new MySqlConnection("Server=127.0.0.1;User ID=root;Password=****;Database=test");
connection.Open();

using var command = new MySqlCommand("select id from t1;", connection);
using var reader = command.ExecuteReader();
var x = new Dictionary<string, object> { };
while (reader.Read())
{
x["name"] = "name";
x["value"] = reader.GetValue(0);
}
Console.WriteLine(JsonSerializer.Serialize(x));
```

输出结果为

```js
{"name":"name","value":1}
```

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

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

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

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

© 2021 V2EX