Golang解析JSON遇到的坑及解決方法
寫(xiě)在前面
在寫(xiě)go的時(shí)候經(jīng)常用到序列化、反序列化,記錄一下遇到過(guò)的坑。
空指針會(huì)被解析成字符串"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一個(gè)空指針的時(shí)候,得到的結(jié)果居然是"null"字符串,我以為是""或者報(bào)錯(cuò)。
還有個(gè)奇怪的坑
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:<nil>
}這個(gè)居然不報(bào)錯(cuò),而是得到空指針p
如果把s隨便換成其他字符串s := "abc",則報(bào)錯(cuò):invalid character 'a' looking for beginning of value,之前我理解的是null對(duì)go來(lái)說(shuō)應(yīng)該跟abc沒(méi)有差別,都是字符串。沒(méi)想到他們是不一樣的,下面來(lái)深究一下json.UnMarshal底層代碼。
在UnMarshal之前它有個(gè)checkValid函數(shù)
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函數(shù)會(huì)check每一個(gè)字符,調(diào)用step函數(shù),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")
}有這么一段代碼,這是處理第一個(gè)字符的,發(fā)現(xiàn)它對(duì)第一個(gè)字符是n有特殊處理并且設(shè)置下一個(gè)字符處理函數(shù)為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')")
}也就是下一個(gè)字符必須是u,再下一個(gè)字符處理函數(shù)為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')")
}也就是下一個(gè)字符必須是l,再下一個(gè)字符處理函數(shù)為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')")
}也就是下一個(gè)字符必須是l,再下一個(gè)字符處理函數(shù)為stateEndValue。
可見(jiàn)checkValid函數(shù)對(duì)true,false等都有特殊處理。使用時(shí)需要注意。
對(duì)于json.Marshal函數(shù),通過(guò)調(diào)試發(fā)現(xiàn)它對(duì)空指針也有特殊處理
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",并且不會(huì)報(bào)錯(cuò)。
int類(lèi)型會(huì)被解析成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)
}
}結(jié)果
k:Name,v:text, vtype:string
k:Age,v:18, vtype:float64
顯然,Age類(lèi)型變成了float64。會(huì)造成什么問(wèn)題呢?當(dāng)int大小超過(guò)6位的時(shí)候就變成了科學(xué)計(jì)數(shù)法 比如Age=1234567, 結(jié)果為
k:Name,v:text, vtype:string
k:Age,v:1.234567e+06, vtype:float64
這個(gè)時(shí)候如果直接將map更新到db,原本是int類(lèi)型的字段變成了float類(lèi)型,就報(bào)錯(cuò)了
到此這篇關(guān)于Golang解析JSON遇到的坑及解決方法的文章就介紹到這了,更多相關(guān)Golang解析JSON內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Go與Rust高性能解析JSON實(shí)現(xiàn)方法示例
- golang解析json數(shù)據(jù)的4種方法總結(jié)
- Go語(yǔ)言學(xué)習(xí)之JSON編碼解析與使用
- Go語(yǔ)言實(shí)現(xiàn)JSON解析的神器詳解
- 一文帶你了解Go語(yǔ)言如何解析JSON
- Go語(yǔ)言JSON解析器gjson使用方法詳解
- Golang實(shí)現(xiàn)解析JSON的三種方法總結(jié)
- golang生成JSON以及解析JSON
- Go?語(yǔ)言?json解析框架與?gjson?詳解
- go語(yǔ)言用八百行代碼實(shí)現(xiàn)一個(gè)JSON解析器
- Go語(yǔ)言實(shí)現(xiàn)JSON解析的方法詳解
- GO中Json解析的幾種方式
相關(guān)文章
Go語(yǔ)言基礎(chǔ)函數(shù)包的使用學(xué)習(xí)
本文通過(guò)一個(gè)實(shí)現(xiàn)加減乘除運(yùn)算的小程序來(lái)介紹go函數(shù)的使用,以及使用函數(shù)的注意事項(xiàng),并引出了對(duì)包的了解和使用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
Golang 發(fā)送http請(qǐng)求時(shí)設(shè)置header的實(shí)現(xiàn)
這篇文章主要介紹了Golang 發(fā)送http請(qǐng)求時(shí)設(shè)置header的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02

