目录
写在前面空指针会被解析成字符串"null"int类型会被解析成float64写在前面
在写go的时候经常用到序列化、反序列化,记录一下遇到过的坑。
空指针会被解析成字符串"null"
type Person struct { Name string Age int } func main() { var p *Person bytes, 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 string Age int } func main() { var p *Person s := `null` err := json.Unmarshal([]byte(s), &p) checkError(err) fmt.Printf("p:%+v\n", p) // p:}
这个居然不报错,而是得到空指针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 = stateBeginStringOrEmpty return s.pushParseState(c, parseObjectKey, scanBeginObject) case "[": s.step = stateBeginValueOrEmpty return s.pushParseState(c, parseArrayValue, scanBeginArray) case """: s.step = stateInString return scanBeginLiteral case "-": s.step = stateNeg return scanBeginLiteral case "0": // beginning of 0.123 s.step = state0 return scanBeginLiteral case "t": // beginning of true s.step = stateT return scanBeginLiteral case "f": // beginning of false s.step = stateF return scanBeginLiteral case "n": // beginning of null s.step = stateN return scanBeginLiteral } if "1" <= c && c <= "9" { // beginning of 1234.5 s.step = state1 return 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 = stateNu return 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 = stateNul return 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 = stateEndValue return 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 string Age 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遇到的坑及解决方法的文章就介绍到这了,更多相关Golang解析JSON内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
X 关闭
X 关闭
- 15G资费不大降!三大运营商谁提供的5G网速最快?中国信通院给出答案
- 2联想拯救者Y70发布最新预告:售价2970元起 迄今最便宜的骁龙8+旗舰
- 3亚马逊开始大规模推广掌纹支付技术 顾客可使用“挥手付”结账
- 4现代和起亚上半年出口20万辆新能源汽车同比增长30.6%
- 5如何让居民5分钟使用到各种设施?沙特“线性城市”来了
- 6AMD实现连续8个季度的增长 季度营收首次突破60亿美元利润更是翻倍
- 7转转集团发布2022年二季度手机行情报告:二手市场“飘香”
- 8充电宝100Wh等于多少毫安?铁路旅客禁止、限制携带和托运物品目录
- 9好消息!京东与腾讯续签三年战略合作协议 加强技术创新与供应链服务
- 10名创优品拟通过香港IPO全球发售4100万股 全球发售所得款项有什么用处?