Golang中JSON遇到的坑如何解决

前端开发   发布日期:2023年07月14日   浏览次数:441

本篇内容主要讲解“Golang中JSON遇到的坑如何解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Golang中JSON遇到的坑如何解决”吧!

空指针会被解析成字符串"null"

  1. type Person struct {
  2. Name string
  3. Age int
  4. }
  5. func main() {
  6. var p *Person
  7. bytes, err := json.Marshal(p)
  8. checkError(err)
  9. fmt.Printf("len:%d, result:%s
  10. ", len(bytes), string(bytes)) // len:4, result:null
  11. }
  12. func checkError(err error) {
  13. if err != nil {
  14. fmt.Printf("err:%+v
  15. ", err)
  16. }
  17. }

json.Marshal一个空指针的时候,得到的结果居然是"null"字符串,我以为是""或者报错。

还有个奇怪的坑

  1. type Person struct {
  2. Name string
  3. Age int
  4. }
  5. func main() {
  6. var p *Person
  7. s := `null`
  8. err := json.Unmarshal([]byte(s), &p)
  9. checkError(err)
  10. fmt.Printf("p:%+v
  11. ", p) // p:<nil>
  12. }

这个居然不报错,而是得到空指针p

如果把s随便换成其他字符串

  1. s := "abc"
,则报错:
  1. invalid character 'a' looking for beginning of value
,之前我理解的是
  1. null
对go来说应该跟
  1. abc
没有差别,都是字符串。没想到他们是不一样的,下面来深究一下json.UnMarshal底层代码。

在UnMarshal之前它有个

  1. checkValid
函数
  1. func checkValid(data []byte, scan *scanner) error {
  2. scan.reset()
  3. for _, c := range data {
  4. scan.bytes++
  5. if scan.step(scan, c) == scanError {
  6. return scan.err
  7. }
  8. }
  9. if scan.eof() == scanError {
  10. return scan.err
  11. }
  12. return nil
  13. }

  1. checkValid
函数会check每一个字符,调用step函数,step初始值是
  1. stateBeginValue
  1. // stateBeginValue is the state at the beginning of the input.
  2. func stateBeginValue(s *scanner, c byte) int {
  3. if isSpace(c) {
  4. return scanSkipSpace
  5. }
  6. switch c {
  7. case '{':
  8. s.step = stateBeginStringOrEmpty
  9. return s.pushParseState(c, parseObjectKey, scanBeginObject)
  10. case '[':
  11. s.step = stateBeginValueOrEmpty
  12. return s.pushParseState(c, parseArrayValue, scanBeginArray)
  13. case '"':
  14. s.step = stateInString
  15. return scanBeginLiteral
  16. case '-':
  17. s.step = stateNeg
  18. return scanBeginLiteral
  19. case '0': // beginning of 0.123
  20. s.step = state0
  21. return scanBeginLiteral
  22. case 't': // beginning of true
  23. s.step = stateT
  24. return scanBeginLiteral
  25. case 'f': // beginning of false
  26. s.step = stateF
  27. return scanBeginLiteral
  28. case 'n': // beginning of null
  29. s.step = stateN
  30. return scanBeginLiteral
  31. }
  32. if '1' <= c && c <= '9' { // beginning of 1234.5
  33. s.step = state1
  34. return scanBeginLiteral
  35. }
  36. return s.error(c, "looking for beginning of value")
  37. }

有这么一段代码,这是处理第一个字符的,发现它对第一个字符是

  1. n
有特殊处理并且设置下一个字符处理函数为stateN
  1. // stateN is the state after reading `n`.
  2. func stateN(s *scanner, c byte) int {
  3. if c == 'u' {
  4. s.step = stateNu
  5. return scanContinue
  6. }
  7. return s.error(c, "in literal null (expecting 'u')")
  8. }

也就是下一个字符必须是

  1. u
,再下一个字符处理函数为stateNu
  1. // stateNu is the state after reading `nu`.
  2. func stateNu(s *scanner, c byte) int {
  3. if c == 'l' {
  4. s.step = stateNul
  5. return scanContinue
  6. }
  7. return s.error(c, "in literal null (expecting 'l')")
  8. }

也就是下一个字符必须是

  1. l
,再下一个字符处理函数为stateNul
  1. // stateNul is the state after reading `nul`.
  2. func stateNul(s *scanner, c byte) int {
  3. if c == 'l' {
  4. s.step = stateEndValue
  5. return scanContinue
  6. }
  7. return s.error(c, "in literal null (expecting 'l')")
  8. }

也就是下一个字符必须是

  1. l
,再下一个字符处理函数为stateEndValue。

可见

  1. checkValid
函数对true,false等都有特殊处理。使用时需要注意。

对于json.Marshal函数,通过调试发现它对空指针也有特殊处理

  1. type ptrEncoder struct {
  2. elemEnc encoderFunc
  3. }
  4. func (pe ptrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
  5. if v.IsNil() {
  6. e.WriteString("null")
  7. return
  8. }
  9. if e.ptrLevel++; e.ptrLevel > startDetectingCyclesAfter {
  10. // We're a large number of nested ptrEncoder.encode calls deep;
  11. // start checking if we've run into a pointer cycle.
  12. ptr := v.Interface()
  13. if _, ok := e.ptrSeen[ptr]; ok {
  14. e.error(&UnsupportedValueError{v, fmt.Sprintf("encountered a cycle via %s", v.Type())})
  15. }
  16. e.ptrSeen[ptr] = struct{}{}
  17. defer delete(e.ptrSeen, ptr)
  18. }
  19. pe.elemEnc(e, v.Elem(), opts)
  20. e.ptrLevel--
  21. }

如果是空指针则返回字符串"null",并且不会报错。

int类型会被解析成float64

  1. type Person struct {
  2. Name string
  3. Age int
  4. }
  5. func main() {
  6. p := &Person{
  7. Name: "text",
  8. Age: 18,
  9. }
  10. bytes, err := json.Marshal(p)
  11. checkError(err)
  12. pMap := make(map[string]interface{})
  13. err = json.Unmarshal(bytes, &pMap)
  14. checkError(err)
  15. for k, v := range pMap {
  16. fmt.Printf("k:%s,v:%+v, vtype:%v
  17. ", k, v, reflect.TypeOf(v))
  18. }
  19. }
  20. func checkError(err error) {
  21. if err != nil {
  22. fmt.Printf("err:%+v
  23. ", err)
  24. }
  25. }

结果

k:Name,v:text, vtype:string
k:Age,v:18, vtype:float64

显然,Age类型变成了float64。会造成什么问题呢?当int大小超过6位的时候就变成了科学计数法 比如Age=1234567, 结果为

k:Name,v:text, vtype:string
k:Age,v:1.234567e+06, vtype:float64

这个时候如果直接将map更新到db,原本是int类型的字段变成了float类型,就报错了

以上就是Golang中JSON遇到的坑如何解决的详细内容,更多关于Golang中JSON遇到的坑如何解决的资料请关注九品源码其它相关文章!