Golang
主页 > 脚本 > Golang >

Go json反序列化“null“的问题解决

2024-02-12 | 佚名 | 点击:

有这么一段代码,可以先看一下有没有什么问题,作用是输入一段json字符串,反序列化成map,然后将另一个inputMap的内容,merge进这个map

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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

        }

    }

    //merge

    for k, v := range inputMap {

        jsonMap[k] = v

    }

    return jsonMap, nil

}

看上去是不是一段很健康的代码?
结合标题再看看呢?
如果输入的json字符串是"null"会发生什么呢?

 

实验

1

2

3

4

5

6

7

8

9

10

func main(){

    inputMap := make(map[string]interface{})

    inputMap["test"] = 1

    outputMap, err := mergeContent("null", inputMap)

    if err != nil {

        fmt.Println("err:", err)

        return

    }

    fmt.Printf("output:%+v ", outputMap)

}

不出意外的话,要出意外了

它说我给一个nil map赋值了,但我明明在反序列化之前给jsonMap初始化了的,原来,jsoniter这个库【其他库没测】在进行json序列化时,会把nil【即指针类型(比如slice、map和*T)的未初始化时的形态】序列化为字符串"null",反序列化时会把字符串"null",如果目标类型是指针类型,则会反序列化为nil

 

其他测试

知道现象和原因之后,我又测试了些其他的东西
需要注意的是fmt很多时候打印nil的指针类型时不会输出nil,比如nil的slice和map,会打印成[]和map[];

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

package main

 

import (

   "fmt"

   jsoniter "github.com/json-iterator/go"

)

 

type Marshaler struct {

   A map[string]interface{}

   B []string

   C [10]int

   D *string

   E *EE

   F string

   g string

}

type EE struct {

   EEE []string

}

 

func main() {

   mal := Marshaler{

      E: &EE{},

   }

   mal1 := &Marshaler{}

   e1 := &EE{}

   e2 := EE{EEE: []string{}}

   var t1 *string

   var t2 []int

   var 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 ", res1)

   fmt.Printf("res2: %+v ", res2)

   fmt.Printf("res3: %+v ", res3)

   fmt.Printf("res4: %+v ", res4)

   fmt.Printf("res5: %+v ", res5)

   fmt.Printf("res6: %+v ", res6)

   fmt.Printf("res7: %+v ", res7)

   fmt.Printf("res8: %+v ", 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 ", params)

   }

 

   if err := jsoniter.Unmarshal([]byte(res6), &mal); err != nil {

      fmt.Println("null Unmarshal err:", err)

   } else {

      fmt.Printf("null Unmarshal Marshaler: %+v ", mal)

   }

 

   if err := jsoniter.Unmarshal([]byte(res6), &mal1); err != nil {

      fmt.Println("null Unmarshal err:", err)

   } else {

      fmt.Printf("null Unmarshal Marshaler: %+v ", mal1)

   }

 

   if err := jsoniter.Unmarshal([]byte(res6), &t4); err != nil {

      fmt.Println("null Unmarshal err:", err)

   } else {

      fmt.Printf("null Unmarshal []string: %+v ", 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: E:0xc000
004510 F: g:}
null Unmarshal Marshaler:
null Unmarshal []string: []
true

补充说明

默认的反序列化是用float64来接数字类型的,原来的数字太大会出现精度丢失问题

"null"用int或float32等基础数字类型来接会变成默认值0

很多json库在反序列化时都会存在精度丢失问题,比如int64的最后几位变成0,是因为不明确json字符串代表的struct的场景下,用map[string]interface{}来接反序列化之后的内容,会默认用float64来接数字类型,“int64是将64bit的数据全部用来存储数据,但是float64需要表达的信息更多,因此float64单纯用于数据存储的位数将小于64bit,这就导致了float64可存储的最大整数是小于int64的。”,理论上数值超过9007199254740991就可能会出现精度缺失,反序列化时需要针对数字类型单独处理【用struct来接,并且保证类型能对应上就不会有以上问题】:

原文链接:
相关文章
最新更新