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

golang通過反射手動實現(xiàn)json序列化的方法

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

一、json

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

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

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

import (
	"encoding/json"
	"fmt"
	"testing"
)
type TestJson struct {
	UserId       int    `json:"user_id"` // 使用結(jié)構(gòu)體標(biāo)簽可以指定字段在 JSON 中的鍵名
	UserNickname string // 如果沒有指定標(biāo)簽,則默認(rèn)使用字段名。
	UserAge      int    `json:"age,omitempty"` // 使用omitempty可以在序列化時忽略空值
}
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 對象)

使用 json.Unmarshal() 函數(shù)可以將一個 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ù)組,再傳入解析后的對象指針
	// 第二個參數(shù)必須是指向目標(biāo)數(shù)據(jù)類型變量的指針,以便函數(shù)能夠修改該變量。
	err := json.Unmarshal([]byte(jsonStr), &tJson)
	if err != nil {
		fmt.Println("解析錯誤:", err)
	} else {
		fmt.Println(tJson)
	}
}

輸出

{1 Jackson 18}

1.3 注意

  • 字段導(dǎo)出:只有導(dǎo)出的字段(即首字母大寫)才能被編碼/解碼。
  • 錯誤處理:始終檢查返回錯誤,以確保數(shù)據(jù)正確處理。
  • 多余的信息:解析的結(jié)構(gòu)體之外的字段會被丟棄
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("解析錯誤:", err)
	} else {
		fmt.Println(tJson)
	}
}

輸出

{1 Jackson 18}

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

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("解析錯誤:", err)
	} else {
		fmt.Println(tJson)
	}
}

輸出

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

二、反射手動實現(xiàn)

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

JSON 序列化中的反射

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

JSON 反序列化中的反射

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

2.1 json簡單序列化

思路:模仿 encoding/json 庫,讀取結(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é)尾的,號
	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("=========自定義實現(xiàn)=========")
	fmt.Println(serializeSimple(tJson1))
	fmt.Println(serializeSimple(tJson2))
}

輸出

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

2.2 json簡單反序列化

思路

  • 切割傳入的字符串,拆分成key value的鍵值對
  • 反射結(jié)構(gòu)體,判斷結(jié)構(gòu)體標(biāo)簽后生成一個字段和值的map
  • 判斷字段類型,set進(jìn)對應(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{}
		// 通過反射獲取字段名
		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", "")
			}
			// 將字段名和值映射起來
			structMap[name] = rValue.Field(i)
		}
		// 按照,切割每個鍵值對
		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() {
			// 注意判斷類型,目前只寫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("解析錯誤:", err)
	} else {
		fmt.Println(tJson1)
	}
	fmt.Println("=========自定義實現(xiàn)=========")
	parseSimple(jsonStr, &tJson2)
	fmt.Println(tJson2)
}

2.3 問題

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

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

2.4 總結(jié)

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

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

單引號 (')

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

雙引號 (")

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

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

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

  • strconv.Atoi 函數(shù)將字符串轉(zhuǎn)換為int類型。如果轉(zhuǎn)換成功,它返回轉(zhuǎn)換后的整數(shù)和nil錯誤;如果失敗,它返回0和錯誤。
  • strconv.ParseInt 函數(shù)將字符串轉(zhuǎn)換為int64類型,并且允許你指定基數(shù)和位大小。如果你需要轉(zhuǎn)換為int類型,你可能需要根據(jù)平臺(32位或64位)來決定如何處理結(jié)果。
func Test8(t *testing.T) {
	str := "123"
	// 第二個參數(shù)是基數(shù),10表示十進(jìn)制
	// 第三個參數(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)換錯誤:", 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 通過反射手動實現(xiàn)json序列化的文章就介紹到這了,更多相關(guān)golang json序列化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • golang sql連接池的實現(xiàn)方法詳解

    golang sql連接池的實現(xiàn)方法詳解

    database/sql是golang的標(biāo)準(zhǔn)庫之一,它提供了一系列接口方法,用于訪問關(guān)系數(shù)據(jù)庫。下面這篇文章主要給大家介紹了關(guān)于golang sql連接池用法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧
    2018-09-09
  • Go隨機數(shù)與UUID生成原理與避坑指南

    Go隨機數(shù)與UUID生成原理與避坑指南

    本文介紹了Go中的隨機數(shù)生成和UUID生成的相關(guān)知識,包括兩種隨機數(shù)生成庫的區(qū)別和使用場景,UUID的版本和優(yōu)缺點,以及在實際工程中如何正確使用這兩種工具的建議,文章強調(diào)了在實際應(yīng)用中,開發(fā)者需要理解背后的原理和設(shè)計理念,并做出合適的選擇,需要的朋友可以參考下
    2026-05-05
  • golang程序進(jìn)度條實現(xiàn)示例詳解

    golang程序進(jìn)度條實現(xiàn)示例詳解

    這篇文章主要為大家介紹了golang程序?qū)崿F(xiàn)進(jìn)度條示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • 淺談Golang的Work Stealing機制

    淺談Golang的Work Stealing機制

    Go的運行時系統(tǒng)使用了一種名為Work Stealing的調(diào)度策略來分配Goroutine到可用線程上執(zhí)行,本文主要介紹了淺談Golang的Work Stealing機制,具有一定的參考價值,感興趣的可以了解一下
    2025-03-03
  • Golang Printf,Sprintf,Fprintf 格式化詳解

    Golang Printf,Sprintf,Fprintf 格式化詳解

    這篇文章主要介紹了Golang Printf,Sprintf,Fprintf 格式化詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • 入門學(xué)習(xí)Go的基本語法

    入門學(xué)習(xí)Go的基本語法

    這篇文章主要介紹了Go語法的基本概念,本文講解了Golang的基本知識、基礎(chǔ)語法、相關(guān)術(shù)語等,需要的朋友可以參考下,希望能幫助到你
    2021-07-07
  • 用go寫的五子棋預(yù)測算法的實現(xiàn)

    用go寫的五子棋預(yù)測算法的實現(xiàn)

    這篇文章主要介紹了用go寫的五子棋預(yù)測算法的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • golang 對私有函數(shù)進(jìn)行單元測試的實例

    golang 對私有函數(shù)進(jìn)行單元測試的實例

    這篇文章主要介紹了golang 對私有函數(shù)進(jìn)行單元測試的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • Go中各種newreader和newbuffer的使用總結(jié)

    Go中各種newreader和newbuffer的使用總結(jié)

    這篇文章主要為大家詳細(xì)介紹了Go語言中各種newreader和newbuffer的使用的相關(guān)資料,文中的示例代碼講解詳細(xì),具有一定的參考價值,感興趣的小伙伴可以了解下
    2023-11-11
  • GO語言實現(xiàn)的http抓包分析工具pproxy介紹

    GO語言實現(xiàn)的http抓包分析工具pproxy介紹

    這篇文章主要介紹了GO語言實現(xiàn)的http抓包分析工具pproxy介紹,本文同時對比了Fiddler、Charles等抓包軟件,需要的朋友可以參考下
    2015-03-03

最新評論

浮梁县| 南雄市| 林西县| 宿松县| 保德县| 平江县| 太原市| 耒阳市| 柳林县| 陆丰市| 灵武市| 开化县| 五家渠市| 越西县| 华安县| 天峨县| 甘肃省| 光泽县| 茂名市| 开阳县| 北宁市| 喀喇沁旗| 文登市| 定边县| 旅游| 敖汉旗| 新巴尔虎左旗| 彭泽县| 大田县| 清新县| 宁南县| 祁东县| 临朐县| 太仓市| 北京市| 贞丰县| 泸溪县| 舒城县| 洞头县| 绍兴市| 扎赉特旗|