欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

golang通過(guò)反射手動(dòng)實(shí)現(xiàn)json序列化的方法

 更新時(shí)間:2024年12月10日 12:17:15   作者:程序猿-瑞瑞  
在 Go 語(yǔ)言中,JSON 序列化和反序列化通常通過(guò)標(biāo)準(zhǔn)庫(kù) encoding/json 來(lái)實(shí)現(xiàn),本文給大家介紹golang  通過(guò)反射手動(dòng)實(shí)現(xiàn)json序列化的方法,感興趣的朋友一起看看吧

一、json

在 Go 語(yǔ)言中,JSON 序列化和反序列化通常通過(guò)標(biāo)準(zhǔn)庫(kù) encoding/json 來(lái)實(shí)現(xiàn)。這個(gè)包提供了簡(jiǎn)單易用的接口來(lái)將 Go 數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換為 JSON 格式字符串(序列化),以及從 JSON 字符串解析出 Go 數(shù)據(jù)結(jié)構(gòu)(反序列化)。

1.1 序列化(將 Go 對(duì)象轉(zhuǎn)換為 JSON)

使用 json.Marshal() 函數(shù)可以將一個(gè) Go 對(duì)象轉(zhuǎn)換為 JSON 字符串。

import (
	"encoding/json"
	"fmt"
	"testing"
)
type TestJson struct {
	UserId       int    `json:"user_id"` // 使用結(jié)構(gòu)體標(biāo)簽可以指定字段在 JSON 中的鍵名
	UserNickname string // 如果沒(méi)有指定標(biāo)簽,則默認(rèn)使用字段名。
	UserAge      int    `json:"age,omitempty"` // 使用omitempty可以在序列化時(shí)忽略空值
}
func Test1(t *testing.T) {
	tJson1 := TestJson{1, "Jackson", 18}
	tJson2 := new(TestJson)
	jsonStr1, _ := json.Marshal(tJson1)
	jsonStr2, _ := json.Marshal(tJson2)
	fmt.Println(string(jsonStr1))
	fmt.Println(string(jsonStr2))
}

輸出

{"user_id":1,"UserNickname":"Jackson","age":18}
{"user_id":0,"UserNickname":""}

美觀打印的序列化

如果需要生成格式良好的、可讀性更高的輸出,可以使用 json.MarshalIndent()

func Test2(t *testing.T) {
	tJson1 := TestJson{1, "Jackson", 18}
	jsonStr1, _ := json.MarshalIndent(tJson1, "", "\t")
	fmt.Println(string(jsonStr1))
}

輸出

{
    "user_id": 1,
    "UserNickname": "Jackson",
    "age": 18
}

1.2 反序列化(將 JSON 轉(zhuǎn)換為 Go 對(duì)象)

使用 json.Unmarshal() 函數(shù)可以將一個(gè) JSON 字符串解析到相應(yīng)的 Go 數(shù)據(jù)結(jié)構(gòu)中。

func Test3(t *testing.T) {
	jsonStr :=
		`{
			"user_id": 1,
			"UserNickname": "Jackson",
			"age": 18
		}`
	var tJson TestJson
	// 將字符串轉(zhuǎn)為字節(jié)數(shù)組,再傳入解析后的對(duì)象指針
	// 第二個(gè)參數(shù)必須是指向目標(biāo)數(shù)據(jù)類型變量的指針,以便函數(shù)能夠修改該變量。
	err := json.Unmarshal([]byte(jsonStr), &tJson)
	if err != nil {
		fmt.Println("解析錯(cuò)誤:", err)
	} else {
		fmt.Println(tJson)
	}
}

輸出

{1 Jackson 18}

1.3 注意

  • 字段導(dǎo)出:只有導(dǎo)出的字段(即首字母大寫(xiě))才能被編碼/解碼。
  • 錯(cuò)誤處理:始終檢查返回錯(cuò)誤,以確保數(shù)據(jù)正確處理。
  • 多余的信息:解析的結(jié)構(gòu)體之外的字段會(huì)被丟棄
func Test5(t *testing.T) {
	jsonStr :=
		`{
			"user_id": 1,
			"UserNickname": "Jackson",
			"age": 18,
			"addr": ["地址1","地址2"],
			"info":{
				"id":"2",
				"name":"a"
			}
		}`
	var tJson TestJson
	err := json.Unmarshal([]byte(jsonStr), &tJson)
	if err != nil {
		fmt.Println("解析錯(cuò)誤:", err)
	} else {
		fmt.Println(tJson)
	}
}

輸出

{1 Jackson 18}

靈活性:對(duì)于未知或動(dòng)態(tài)數(shù)據(jù),可以考慮使用 map 或 interface{} 來(lái)接收解碼結(jié)果,但這會(huì)喪失一些類型安全特性。

func Test4(t *testing.T) {
	jsonStr :=
		`{
			"user_id": 1,
			"UserNickname": "Jackson",
			"age": 18,
			"addr": ["地址1","地址2"],
			"info":{
				"id":"2",
				"name":"a"
			}
		}`
	var tJson map[string]any
	err := json.Unmarshal([]byte(jsonStr), &tJson)
	if err != nil {
		fmt.Println("解析錯(cuò)誤:", err)
	} else {
		fmt.Println(tJson)
	}
}

輸出

map[UserNickname:Jackson addr:[地址1 地址2] age:18 info:map[id:2 name:a] user_id:1]

二、反射手動(dòng)實(shí)現(xiàn)

encoding/json 包在內(nèi)部大量使用了反射來(lái)實(shí)現(xiàn)其功能。

JSON 序列化中的反射

  • 在調(diào)用 json.Marshal() 時(shí),Go 使用反射來(lái)檢查傳入對(duì)象的類型。
  • 通過(guò) reflect.TypeOf()reflect.ValueOf() 獲取類型信息和實(shí)際值。
  • 遍歷結(jié)構(gòu)體字段,讀取標(biāo)簽(如 json:"name"),并根據(jù)字段類型生成相應(yīng)的 JSON 字符串。

JSON 反序列化中的反射

  • 在調(diào)用 json.Unmarshal() 時(shí),Go 使用目標(biāo)變量指針,通過(guò)反射確定需要填充的數(shù)據(jù)結(jié)構(gòu)。
  • 根據(jù) JSON 數(shù)據(jù)中的鍵名,通過(guò)標(biāo)簽映射找到對(duì)應(yīng)結(jié)構(gòu)體字段,并設(shè)置其值。

2.1 json簡(jiǎn)單序列化

思路:模仿 encoding/json 庫(kù),讀取結(jié)構(gòu)體標(biāo)簽指定序列化的字段名

  • 反射傳入的結(jié)構(gòu)體
  • 獲取字段
  • 判斷字段的標(biāo)簽
  • 拼接字符串
func serializeSimple(data interface{}) string {
	var resultStr string = "{"
	//1、反射傳入的結(jié)構(gòu)體
	reflectDataValue := reflect.ValueOf(data)
	reflectDataType := reflectDataValue.Type()
	if reflectDataValue.Kind() == reflect.Ptr {
		reflectDataValue = reflectDataValue.Elem()
	}
	if reflectDataType.Kind() == reflect.Ptr {
		reflectDataType = reflectDataType.Elem()
	}
	//2、獲取字段
	for i := 0; i < reflectDataType.NumField(); i++ {
		field := reflectDataType.Field(i)
		// 字段名
		var filedName = field.Name
		// 字段的值
		filedValue := reflectDataValue.Field(i).Interface()
		//3、判斷字段的標(biāo)簽
		if value, ok := field.Tag.Lookup("json"); ok {
			// 如果有json標(biāo)簽,則使用其定義的命名
			filedName = strings.ReplaceAll(value, ",omitempty", "")
			// 是否忽略空值
			if strings.Contains(value, "omitempty") {
				if filedValue == "" || filedValue == 0 {
					continue
				}
			}
		}
		// 拼接json
		resultStr += fmt.Sprintf("\"%s\":\"%v\"", filedName, filedValue)
		resultStr += ","
	}
	//4、拼接字符串
	resultStr += "}"
	// 去掉結(jié)尾的,號(hào)
	return strings.ReplaceAll(resultStr, ",}", "}")
}
func Test6(t *testing.T) {
	tJson1 := TestJson{1, "Jackson", 18}
	tJson2 := new(TestJson)
	jsonStr1, _ := json.Marshal(tJson1)
	jsonStr2, _ := json.Marshal(tJson2)
	fmt.Println(string(jsonStr1))
	fmt.Println(string(jsonStr2))
	fmt.Println("=========自定義實(shí)現(xiàn)=========")
	fmt.Println(serializeSimple(tJson1))
	fmt.Println(serializeSimple(tJson2))
}

輸出

{"user_id":1,"UserNickname":"Jackson","age":18}
{"user_id":0,"UserNickname":""}
=========自定義實(shí)現(xiàn)=========
{"user_id":"1","UserNickname":"Jackson","age":"18"}
{"user_id":"0","UserNickname":""}

2.2 json簡(jiǎn)單反序列化

思路

  • 切割傳入的字符串,拆分成key value的鍵值對(duì)
  • 反射結(jié)構(gòu)體,判斷結(jié)構(gòu)體標(biāo)簽后生成一個(gè)字段和值的map
  • 判斷字段類型,set進(jìn)對(duì)應(yīng)類型的值
func parseSimple(str string, dataTemp interface{}) {
	// 判斷是否標(biāo)準(zhǔn)的json格式數(shù)據(jù)
	if str != "" && str[0] == '{' && str[len(str)-1] == '}' {
		// 替換掉前后的{}
		str = strings.ReplaceAll(strings.ReplaceAll(str, "{", ""), "}", "")
		// 將結(jié)構(gòu)體的標(biāo)簽解析后,塞入map中備用 :map [字段名] 字段地址
		structMap := map[string]reflect.Value{}
		// 通過(guò)反射獲取字段名
		rValue := reflect.ValueOf(dataTemp)
		if rValue.Kind() == reflect.Ptr {
			rValue = rValue.Elem()
		}
		rType := rValue.Type()
		for i := 0; i < rType.NumField(); i++ {
			name := rType.Field(i).Name
			// 如果有定義標(biāo)簽,則使用標(biāo)簽的字段名
			if lookup, ok := rType.Field(i).Tag.Lookup("json"); ok {
				name = strings.ReplaceAll(lookup, ",omitempty", "")
			}
			// 將字段名和值映射起來(lái)
			structMap[name] = rValue.Field(i)
		}
		// 按照,切割每個(gè)鍵值對(duì)
		splitList := strings.Split(str, ",")
		for i := range splitList {
			s := splitList[i]
			// 按照:切割出key 和 value
			keyValue := strings.Split(s, ":")
			key := keyValue[0]
			key = strings.ReplaceAll(strings.TrimSpace(key), "\"", "") // 去除前后的空格
			value := keyValue[1]
			value = strings.ReplaceAll(strings.TrimSpace(value), "\"", "")
			// 按照key將value塞回結(jié)構(gòu)體
			switch structMap[key].Type().Kind() {
			// 注意判斷類型,目前只寫(xiě)int和string,其他的類似
			case reflect.Int:
				intValue, _ := strconv.Atoi(value)
				structMap[key].SetInt(int64(intValue))
			case reflect.String:
				structMap[key].SetString(value)
			default:
				panic("暫不支持的數(shù)據(jù)類型")
			}
		}
	}
}
func Test7(t *testing.T) {
	jsonStr :=
		`{
			"user_id": 1,
			"UserNickname": "Jackson",
			"age": 18
		}`
	var tJson1 TestJson
	var tJson2 TestJson
	err := json.Unmarshal([]byte(jsonStr), &tJson1)
	if err != nil {
		fmt.Println("解析錯(cuò)誤:", err)
	} else {
		fmt.Println(tJson1)
	}
	fmt.Println("=========自定義實(shí)現(xiàn)=========")
	parseSimple(jsonStr, &tJson2)
	fmt.Println(tJson2)
}

2.3 問(wèn)題

  • 只能處理結(jié)構(gòu)體類型的數(shù)據(jù),不支持嵌套復(fù)雜數(shù)據(jù)結(jié)構(gòu)(如切片或映射)
  • 在序列化的實(shí)現(xiàn)中,整數(shù)或布爾值會(huì)被轉(zhuǎn)換為帶引號(hào)的字符串形式。
  • 復(fù)雜的結(jié)構(gòu)體不支持,可能會(huì)出問(wèn)題

所以手動(dòng)實(shí)現(xiàn) JSON 序列化和反序列化只是幫助我們更好地理解數(shù)據(jù)格式與程序語(yǔ)言之間的映射關(guān)系,在實(shí)際開(kāi)發(fā)中,使用標(biāo)準(zhǔn)庫(kù) encoding/json 是更高效且可靠的方法,因?yàn)樗呀?jīng)考慮了許多復(fù)雜情況,并進(jìn)行了性能優(yōu)化。

2.4 總結(jié)

2.4.1 單引號(hào)和雙引號(hào)定義的字符串有什么區(qū)別

在 json簡(jiǎn)單反序列化的例子中,使用了單引號(hào)和雙引號(hào)定義不同類型的字符數(shù)據(jù)。具體的區(qū)別是啥呢?

單引號(hào) (')

  • 單引號(hào)用于表示一個(gè)字符(rune)。在 Go 中,rune 是一個(gè)別名,代表 int32 類型,用于表示 Unicode 碼點(diǎn)。
  • 一個(gè)用單引號(hào)括起來(lái)的字符常量只能包含一個(gè)字符。例如:'a', '中', '??'。

雙引號(hào) (")

  • 雙引號(hào)用于定義字符串(string)。字符串是由一系列字節(jié)組成的數(shù)據(jù)類型,可以包含零個(gè)或多個(gè)字符。
  • 字符串可以包括轉(zhuǎn)義序列,例如:\n, \t, \" 等。
  • 例如:“hello”, “世界”, “Go is fun\n”.

2.4.2 string如何轉(zhuǎn)為int

在 json簡(jiǎn)單反序列化的例子中,使用了strconv.Atoi函數(shù)將字符串(string)轉(zhuǎn)換為整數(shù)(int),除此之外還有strconv.ParseInt

  • strconv.Atoi 函數(shù)將字符串轉(zhuǎn)換為int類型。如果轉(zhuǎn)換成功,它返回轉(zhuǎn)換后的整數(shù)和nil錯(cuò)誤;如果失敗,它返回0和錯(cuò)誤。
  • strconv.ParseInt 函數(shù)將字符串轉(zhuǎn)換為int64類型,并且允許你指定基數(shù)和位大小。如果你需要轉(zhuǎn)換為int類型,你可能需要根據(jù)平臺(tái)(32位或64位)來(lái)決定如何處理結(jié)果。
func Test8(t *testing.T) {
	str := "123"
	// 第二個(gè)參數(shù)是基數(shù),10表示十進(jìn)制
	// 第三個(gè)參數(shù)是位大小,0表示int64,32表示int32,使用32或64取決于你的系統(tǒng)架構(gòu)
	num, err := strconv.ParseInt(str, 10, 0)
	if err != nil {
		fmt.Println("轉(zhuǎn)換錯(cuò)誤:", err)
	} else {
		fmt.Println("轉(zhuǎn)換結(jié)果:", num)
		// 如果你需要int32,可以這樣轉(zhuǎn)換
		num32 := int32(num)
		fmt.Println("轉(zhuǎn)換為int32結(jié)果:", num32)
		// 如果你需要int,可以這樣轉(zhuǎn)換(注意:在32位系統(tǒng)上這將是int32,在64位系統(tǒng)上這將是int64)
		numInt := int(num)
		fmt.Println("轉(zhuǎn)換為int結(jié)果:", numInt)
	}
}

輸出:

轉(zhuǎn)換結(jié)果: 123
轉(zhuǎn)換為int32結(jié)果: 123
轉(zhuǎn)換為int結(jié)果: 123

到此這篇關(guān)于golang 通過(guò)反射手動(dòng)實(shí)現(xiàn)json序列化的文章就介紹到這了,更多相關(guān)golang json序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • go語(yǔ)言goto語(yǔ)句跳轉(zhuǎn)到指定的標(biāo)簽實(shí)現(xiàn)方法

    go語(yǔ)言goto語(yǔ)句跳轉(zhuǎn)到指定的標(biāo)簽實(shí)現(xiàn)方法

    這篇文章主要介紹了go語(yǔ)言goto語(yǔ)句跳轉(zhuǎn)到指定的標(biāo)簽實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • golang 使用time包獲取時(shí)間戳與日期格式化操作

    golang 使用time包獲取時(shí)間戳與日期格式化操作

    這篇文章主要介紹了golang 使用time包獲取時(shí)間戳與日期格式化操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • 使用systemd部署和守護(hù)golang應(yīng)用程序的操作方法

    使用systemd部署和守護(hù)golang應(yīng)用程序的操作方法

    systemd是一個(gè)流行的守護(hù)進(jìn)程管理器,可以輕松管理服務(wù)的啟動(dòng)、停止、重啟等操作,讓我們的應(yīng)用程序始終保持在線,本文介紹了如何使用systemd部署和守護(hù)golang應(yīng)用程序,感興趣的朋友一起看看吧
    2023-10-10
  • 在Golang中使用Redis的方法示例

    在Golang中使用Redis的方法示例

    這篇文章主要介紹了在Golang中使用Redis的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-06-06
  • Go語(yǔ)言二維數(shù)組的傳參方式

    Go語(yǔ)言二維數(shù)組的傳參方式

    這篇文章主要介紹了Go語(yǔ)言二維數(shù)組的傳參方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • go語(yǔ)言入門(mén)環(huán)境搭建及GoLand安裝教程詳解

    go語(yǔ)言入門(mén)環(huán)境搭建及GoLand安裝教程詳解

    這篇文章主要介紹了go語(yǔ)言入門(mén)環(huán)境搭建及GoLand安裝教程詳解,需要的朋友可以參考下
    2020-12-12
  • Go語(yǔ)言中比較兩個(gè)map[string]interface{}是否相等

    Go語(yǔ)言中比較兩個(gè)map[string]interface{}是否相等

    本文主要介紹了Go語(yǔ)言中比較兩個(gè)map[string]interface{}是否相等,我們可以將其轉(zhuǎn)化成順序一樣的 slice ,然后再轉(zhuǎn)化未json,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-08-08
  • golang validator庫(kù)參數(shù)校驗(yàn)實(shí)用技巧干貨

    golang validator庫(kù)參數(shù)校驗(yàn)實(shí)用技巧干貨

    這篇文章主要為大家介紹了validator庫(kù)參數(shù)校驗(yàn)實(shí)用技巧干貨,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • go語(yǔ)言中使用ent做關(guān)聯(lián)查詢的示例詳解

    go語(yǔ)言中使用ent做關(guān)聯(lián)查詢的示例詳解

    go語(yǔ)言的ent框架是facebook開(kāi)源的ORM框架,是go語(yǔ)言開(kāi)發(fā)中的常用框架,而關(guān)聯(lián)查詢又是日常開(kāi)發(fā)中的常見(jiàn)數(shù)據(jù)庫(kù)操作,故文本給出一個(gè)使用ent做關(guān)聯(lián)查詢的使用示例,需要的朋友可以參考下
    2024-02-02
  • GO語(yǔ)言make和new關(guān)鍵字的區(qū)別

    GO語(yǔ)言make和new關(guān)鍵字的區(qū)別

    本篇文章來(lái)介紹一道非常常見(jiàn)的面試題,到底有多常見(jiàn)呢?可能很多面試的開(kāi)場(chǎng)白就是由此開(kāi)始的。那就是?new?和?make?這兩個(gè)內(nèi)置函數(shù)的區(qū)別,希望對(duì)大家有所幫助
    2023-04-04

最新評(píng)論