最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Golang解析JSON遇到的坑及解決方法

 更新時(shí)間:2023年02月26日 11:34:20   作者:nil  
這篇文章主要為大家介紹了Golang解析JSON時(shí)會(huì)遇到的一些坑及解決方法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語(yǔ)言有一點(diǎn)的幫助,需要的可以參考一下

寫(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)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Go語(yǔ)言中數(shù)組,切片和映射的使用

    詳解Go語(yǔ)言中數(shù)組,切片和映射的使用

    Arrays (數(shù)組), Slices (切片) 和 Maps (映射) 是常見(jiàn)的一類(lèi)數(shù)據(jù)結(jié)構(gòu)。這篇文章將為大家詳細(xì)介紹一下Go語(yǔ)言中數(shù)組,切片和映射的使用,感興趣的可以學(xué)習(xí)一下
    2022-07-07
  • golang字符串匹配算法解讀

    golang字符串匹配算法解讀

    文章介紹了字符串匹配算法的原理,特別是Knuth-Morris-Pratt(KMP)算法,該算法通過(guò)構(gòu)建模式串的前綴表來(lái)減少匹配時(shí)的不必要的字符比較,從而提高效率,在Golang中實(shí)現(xiàn)KMP算法時(shí),需要構(gòu)建前綴表并在文本串中進(jìn)行匹配
    2025-02-02
  • golang如何操作csv文件詳解

    golang如何操作csv文件詳解

    這篇文章主要給大家介紹了關(guān)于golang如何操作csv文件的相關(guān)資料,以及使用Golang導(dǎo)出CSV數(shù)據(jù)并解決數(shù)據(jù)亂碼問(wèn)題的解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02
  • Go?語(yǔ)言結(jié)構(gòu)體鏈表的基本操作

    Go?語(yǔ)言結(jié)構(gòu)體鏈表的基本操作

    鏈表是一種物理存儲(chǔ)單元上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過(guò)鏈表中的指針鏈接次序?qū)崿F(xiàn)的,這篇文章主要介紹了Go?語(yǔ)言結(jié)構(gòu)體鏈表,需要的朋友可以參考下
    2022-04-04
  • Go語(yǔ)言基礎(chǔ)函數(shù)包的使用學(xué)習(xí)

    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
  • 淺談go build后加文件和目錄的區(qū)別

    淺談go build后加文件和目錄的區(qū)別

    這篇文章主要介紹了淺談go build后加文件和目錄的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • Go語(yǔ)言接口與多態(tài)詳細(xì)介紹

    Go語(yǔ)言接口與多態(tài)詳細(xì)介紹

    Go語(yǔ)言的接口類(lèi)型是一組方法定義的集合,它體現(xiàn)了多態(tài)性、高內(nèi)聚和低耦合的設(shè)計(jì)思想,接口通過(guò)interface關(guān)鍵字定義,無(wú)需實(shí)現(xiàn)具體方法,任何實(shí)現(xiàn)了接口所有方法的類(lèi)型即視為實(shí)現(xiàn)了該接口,感興趣的朋友一起看看吧
    2024-09-09
  • Golang 發(fā)送http請(qǐng)求時(shí)設(shè)置header的實(shí)現(xiàn)

    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
  • golang?JSON技巧小結(jié)

    golang?JSON技巧小結(jié)

    本文介紹了在Go語(yǔ)言中使用JSON時(shí)的一些小技巧,包括如何將字符串轉(zhuǎn)換為數(shù)字、臨時(shí)忽略空字段、臨時(shí)添加額外字段、粘合兩個(gè)struct、改名struct字段、支持不同類(lèi)型的數(shù)字,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • go語(yǔ)言中匿名函數(shù)的作用域陷阱詳解

    go語(yǔ)言中匿名函數(shù)的作用域陷阱詳解

    GO語(yǔ)言的匿名函數(shù)(anonymous?function),其實(shí)就是閉包.是指不需要定義函數(shù)名的一種函數(shù)實(shí)現(xiàn)方式,下面這篇文章主要給大家介紹了關(guān)于go語(yǔ)言中匿名函數(shù)作用域陷阱的相關(guān)資料,需要的朋友可以參考下
    2022-05-05

最新評(píng)論

三台县| 武定县| 汝南县| 田林县| 柯坪县| 莱州市| 玉屏| 临澧县| 河北区| 佛山市| 泸州市| 平乐县| 普安县| 建湖县| 当雄县| 阜南县| 华阴市| 德保县| 万宁市| 读书| 延边| 高安市| 维西| 福州市| 诸暨市| 弥渡县| 临西县| 宽城| 肥东县| 息烽县| 开平市| 汾西县| 江西省| 青州市| 海城市| 萨迦县| 临桂县| 广安市| 平谷区| 凭祥市| 界首市|