分类:debug| 发布时间:2024-06-26 09:49:00
在我们项目中有这么一段代码:
var buf = []byte(`{"id": 123, "method": "listDir", "params": {}}`)
var v map[string]interface{}
checkError(json.Unmarshal(buf, &v))
// do something
out, err := json.MarshalIndent(v, "", " ")
checkError(err)
fmt.Println(string(out))
一直工作正常,输出为:
{
"id": 123,
"method": "listDir",
"params": {}
}
最近需求变更,id 字段变成 64位整型:
var buf = []byte(`{"id": 3424234234234233434, "method": "listDir", "params": {}}`)
var v map[string]interface{}
checkError(json.Unmarshal(buf, &v))
// do something
out, err := json.MarshalIndent(v, "", " ")
checkError(err)
fmt.Println(string(out))
输出为:
{
"id": 3424234234234233300,
"method": "listDir",
"params": {}
}
发现输出的 id 跟原来的不一致
原因是反序列化为 interface{} 时会将数字类型转换为 float64,我们写个简单的代码验证下:
var buf = []byte(`{"id": 3424234234234233434, "method": "listDir", "params": {}}`)
var v map[string]interface{}
checkError(json.Unmarshal(buf, &v))
fmt.Printf("%v, %s\n", v, reflect.TypeOf(v["id"]).String())
输出为:
map[id:3.4242342342342333e+18 method:listDir params:map[]], float64
在 JSON 格式固定的情况下,比较好的方案是明确指定 id 的类型:
var buf = []byte(`{"id": 3424234234234233434, "method": "listDir", "params": {}}`)
type DummyReq struct {
Id int64 `json:"id"`
Method string `json:"method"`
Params interface{} `json:"params"`
}
var v DummyReq
checkError(json.Unmarshal(buf, &v))
// do something
fmt.Printf("%v, %s\n", v, reflect.TypeOf(v.Id).String())
输出:
{3424234234234233434 listDir map[]}, int64
对于格式多变的情况需要先解析 JSON,根据不同格式的 JSON 解析成不同的 struct, 比如在我们的例子中 method 不同,里面的params 也不同,需要定义大量的 struct,然后根据 method 的值,反序列化成不同的 struct。
这种方法工作量显然比较大,除了上述方法还可以使用如下方案:
var buf = []byte(`{"id": 3424234234234233434, "method": "listDir", "params": {}}`)
var v map[string]interface{}
reader := bytes.NewReader(buf)
decoder := json.NewDecoder(reader)
decoder.UseNumber()
checkError(decoder.Decode(&v))
fmt.Printf("%v, %s\n", v, reflect.TypeOf(v["id"]).String())
结果为:
map[id:3424234234234233434 method:listDir params:map[]], json.Number
这样反序列化时就会将数字解析成 json.Number 类型,不会发生信息丢失的问题