本篇内容主要讲解“Golang中JSON遇到的坑如何解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Golang中JSON遇到的坑如何解决”吧!
空指针会被解析成字符串"null"
type Person struct {Name stringAge int}func main() {var p *Personbytes, err := json.Marshal(p)checkError(err)fmt.Printf("len:%d, result:%s\n", len(bytes), string(bytes)) // len:4, result:null}func checkError(err error) {if err != nil {fmt.Printf("err:%+v\n", err)}}
json.Marshal一个空指针的时候,得到的结果居然是"null"字符串,我以为是""或者报错。
还有个奇怪的坑
type Person struct {Name stringAge int}func main() {var p *Persons := `null`err := json.Unmarshal([]byte(s), &p)checkError(err)fmt.Printf("p:%+v\n", p) // p:<nil>}
这个居然不报错,而是得到空指针p
如果把s随便换成其他字符串s := "abc"
,则报错:invalid character 'a' looking for beginning of value
,之前我理解的是null
对go来说应该跟abc
没有差别,都是字符串。没想到他们是不一样的,下面来深究一下json.UnMarshal底层代码。
在UnMarshal之前它有个checkValid
函数
func checkValid(data []byte, scan *scanner) error {scan.reset()for _, c := range data {scan.bytes++if scan.step(scan, c) == scanError {return scan.err}}if scan.eof() == scanError {return scan.err}return nil}
checkValid
函数会check每一个字符,调用step函数,step初始值是stateBeginValue
// stateBeginValue is the state at the beginning of the input.func stateBeginValue(s *scanner, c byte) int {if isSpace(c) {return scanSkipSpace}switch c {case '{':s.step = stateBeginStringOrEmptyreturn s.pushParseState(c, parseObjectKey, scanBeginObject)case '[':s.step = stateBeginValueOrEmptyreturn s.pushParseState(c, parseArrayValue, scanBeginArray)case '"':s.step = stateInStringreturn scanBeginLiteralcase '-':s.step = stateNegreturn scanBeginLiteralcase '0': // beginning of 0.123s.step = state0return scanBeginLiteralcase 't': // beginning of trues.step = stateTreturn scanBeginLiteralcase 'f': // beginning of falses.step = stateFreturn scanBeginLiteralcase 'n': // beginning of nulls.step = stateNreturn scanBeginLiteral}if '1' <= c && c <= '9' { // beginning of 1234.5s.step = state1return scanBeginLiteral}return s.error(c, "looking for beginning of value")}
有这么一段代码,这是处理第一个字符的,发现它对第一个字符是n
有特殊处理并且设置下一个字符处理函数为stateN
// stateN is the state after reading `n`.func stateN(s *scanner, c byte) int {if c == 'u' {s.step = stateNureturn scanContinue}return s.error(c, "in literal null (expecting 'u')")}
也就是下一个字符必须是u
,再下一个字符处理函数为stateNu
// stateNu is the state after reading `nu`.func stateNu(s *scanner, c byte) int {if c == 'l' {s.step = stateNulreturn scanContinue}return s.error(c, "in literal null (expecting 'l')")}
也就是下一个字符必须是l
,再下一个字符处理函数为stateNul
// stateNul is the state after reading `nul`.func stateNul(s *scanner, c byte) int {if c == 'l' {s.step = stateEndValuereturn scanContinue}return s.error(c, "in literal null (expecting 'l')")}
也就是下一个字符必须是l
,再下一个字符处理函数为stateEndValue。
可见checkValid
函数对true,false等都有特殊处理。使用时需要注意。
对于json.Marshal函数,通过调试发现它对空指针也有特殊处理
type ptrEncoder struct {elemEnc encoderFunc}func (pe ptrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {if v.IsNil() {e.WriteString("null")return}if e.ptrLevel++; e.ptrLevel > startDetectingCyclesAfter {// We're a large number of nested ptrEncoder.encode calls deep;// start checking if we've run into a pointer cycle.ptr := v.Interface()if _, ok := e.ptrSeen[ptr]; ok {e.error(&UnsupportedValueError{v, fmt.Sprintf("encountered a cycle via %s", v.Type())})}e.ptrSeen[ptr] = struct{}{}defer delete(e.ptrSeen, ptr)}pe.elemEnc(e, v.Elem(), opts)e.ptrLevel--}
如果是空指针则返回字符串"null",并且不会报错。
int类型会被解析成float64
type Person struct {Name stringAge int}func main() {p := &Person{Name: "text",Age: 18,}bytes, err := json.Marshal(p)checkError(err)pMap := make(map[string]interface{})err = json.Unmarshal(bytes, &pMap)checkError(err)for k, v := range pMap {fmt.Printf("k:%s,v:%+v, vtype:%v\n", k, v, reflect.TypeOf(v))}}func checkError(err error) {if err != nil {fmt.Printf("err:%+v\n", err)}}
结果
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遇到的坑如何解决”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!