网站建设 客户拜访/推广软件排行榜前十名
Go json反序列化"null"踩坑
有这么一段代码,可以先看一下有没有什么问题,作用是输入一段json字符串,反序列化成map,然后将另一个inputMap的内容,merge进这个map
func mergeContent(inputJson string, inputMap map[string]interface{}) (map[string]interface{}, error) {jsonMap := make(map[string]interface{})if inputJson != "" {decoder := jsoniter.NewDecoder(strings.NewReader(inputJson))decoder.UseNumber()if err := decoder.Decode(&jsonMap); err != nil {return nil, err}}//mergefor k, v := range inputMap {jsonMap[k] = v}return jsonMap, nil
}
看上去是不是一段很健康的代码?
结合标题再看看呢?
如果输入的json字符串是"null"会发生什么呢?
实验
func main(){inputMap := make(map[string]interface{})inputMap["test"] = 1outputMap, err := mergeContent("null", inputMap)if err != nil {fmt.Println("err:", err)return}fmt.Printf("output:%+v\n", outputMap)
}
不出意外的话,要出意外了
它说我给一个nil map赋值了,但我明明在反序列化之前给jsonMap初始化了的,原来,jsoniter这个库【其他库没测】在进行json序列化
时,会把nil
【即指针类型(比如slice、map和*T)的未初始化时的形态】序列化为字符串"null",反序列化
时会把字符串"null"
,如果目标类型是指针类型,则会反序列化为nil
其他测试
知道现象和原因之后,我又测试了些其他的东西
需要注意的是fmt很多时候打印nil的指针类型时不会输出nil,比如nil的slice和map,会打印成[]和map[]
;
package mainimport ("fmt"jsoniter "github.com/json-iterator/go"
)type Marshaler struct {A map[string]interface{}B []stringC [10]intD *stringE *EEF stringg string
}
type EE struct {EEE []string
}func main() {mal := Marshaler{E: &EE{},}mal1 := &Marshaler{}e1 := &EE{}e2 := EE{EEE: []string{}}var t1 *stringvar t2 []intvar t3 map[string]interface{}var t4 = make([]string, 0)res1, _ := jsoniter.MarshalToString(mal)res2, _ := jsoniter.MarshalToString(mal1)res3, _ := jsoniter.MarshalToString(e1)res4, _ := jsoniter.MarshalToString(e2)res5, _ := jsoniter.MarshalToString(t1)res6, _ := jsoniter.MarshalToString(t2)res7, _ := jsoniter.MarshalToString(t3)res8, _ := jsoniter.MarshalToString(t4)fmt.Printf("res1: %+v\n", res1)fmt.Printf("res2: %+v\n", res2)fmt.Printf("res3: %+v\n", res3)fmt.Printf("res4: %+v\n", res4)fmt.Printf("res5: %+v\n", res5)fmt.Printf("res6: %+v\n", res6)fmt.Printf("res7: %+v\n", res7)fmt.Printf("res8: %+v\n", res8)params := make(map[string]interface{})if err := jsoniter.Unmarshal([]byte(res6), ¶ms); err != nil {fmt.Println("null Unmarshal err:", err)} else {fmt.Printf("null Unmarshal map: %+v\n", params)}if err := jsoniter.Unmarshal([]byte(res6), &mal); err != nil {fmt.Println("null Unmarshal err:", err)} else {fmt.Printf("null Unmarshal Marshaler: %+v\n", mal)}if err := jsoniter.Unmarshal([]byte(res6), &mal1); err != nil {fmt.Println("null Unmarshal err:", err)} else {fmt.Printf("null Unmarshal Marshaler: %+v\n", mal1)}if err := jsoniter.Unmarshal([]byte(res6), &t4); err != nil {fmt.Println("null Unmarshal err:", err)} else {fmt.Printf("null Unmarshal []string: %+v\n", t4)fmt.Println(t4 == nil)}
}
输出:
res1: {"A":null,"B":null,"C":[0,0,0,0,0,0,0,0,0,0],"D":null,"E":{"EEE":null},"F"
:""}
res2: {"A":null,"B":null,"C":[0,0,0,0,0,0,0,0,0,0],"D":null,"E":null,"F":""}
res3: {"EEE":null}
res4: {"EEE":[]}
res5: null
res6: null
res7: null
res8: []
//下面的打印不够准确,看debug截图
null Unmarshal map: map[]
null Unmarshal Marshaler: {A:map[] B:[] C:[0 0 0 0 0 0 0 0 0 0] D:<nil> E:0xc000
004510 F: g:}
null Unmarshal Marshaler: <nil>
null Unmarshal []string: []
true
补充说明
- 默认的反序列化是用float64来接数字类型的,原来的数字太大会出现精度丢失问题 示例
- "null"用int或float32等基础数字类型来接会变成默认值0