<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在寫go的時候經常用到序列化、反序列化,記錄一下遇到過的坑。
type Person struct { Name string Age int } func main() { var p *Person bytes, err := json.Marshal(p) checkError(err) fmt.Printf("len:%d, result:%sn", len(bytes), string(bytes)) // len:4, result:null } func checkError(err error) { if err != nil { fmt.Printf("err:%+vn", 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:%+vn", 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 = 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",並且不會報錯。
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:%vn", k, v, reflect.TypeOf(v)) } } func checkError(err error) { if err != nil { fmt.Printf("err:%+vn", 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內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45