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

go使用mapstructure解析json的實現實例

 更新時間:2025年11月21日 11:38:05   作者:-睡到自然醒~  
本文主要介紹了go使用mapstructure解析json的實現實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

介紹

先來介紹一下 mapstructure 這個庫主要用來做什么的吧,官網是這么介紹的:

mapstructure 是一個 Go 庫,用于將通用映射值解碼為結構,反之亦然,同時提供有用的錯誤處理。
該庫在解碼數據流(JSON、Gob 等)中的值時最為有用,因為在讀取部分數據之前,您并不十分清楚底層數據的結構。因此,您可以讀取 map[string]interface{} 并使用此庫將其解碼為適當的本地 Go 底層結構。

簡單來說,它擅長解析一些我們并不十分清楚底層數據結構的數據流到我們定義的結構體中。
下面我們通過幾個例子來簡單介紹一下 mapstructure 怎么使用。

例子

普通形式

func normalDecode() {
	type Person struct {
		Name   string
		Age    int
		Emails []string
		Extra  map[string]string
	}

	// 此輸入可以來自任何地方,但通常來自諸如解碼 JSON 之類的東西,我們最初不太確定結構。
	input := map[string]interface{}{
		"name":   "Tim",
		"age":    31,
		"emails": []string{"one@gmail.com", "two@gmail.com", "three@gmail.com"},
		"extra": map[string]string{
			"twitter": "Tim",
		},
	}

	var result Person
	err := mapstructure.Decode(input, &result)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%#v\n", result)
}

輸出:

main.Person{Name:"Tim", Age:31, Emails:[]string{"one@gmail.com", "two@gmail.com", "three@gmail.com"}, Extra:map[string]string{"twitter":"Tim"}}

這個方式應該是我們最經常使用的,非常簡單的將 map[string]interface{} 映射到我們的結構體中。
在這里,我們并沒有指定每個 field 的 tag,讓 mapstructure 自動去映射。
如果我們的 input 是一個 json 字符串,那么我們需要將 json 字符串解析為 map[string]interface{} 之后,再將其映射到我們的結構體中。

func jsonDecode() {
	var jsonStr = `{
	"name": "Tim",
	"age": 31,
	"gender": "male"
}`

	type Person struct {
		Name   string
		Age    int
		Gender string
	}
	m := make(map[string]interface{})
	err := json.Unmarshal([]byte(jsonStr), &m)
	if err != nil {
		panic(err)
	}

	var result Person
	err = mapstructure.Decode(m, &result)
	if err != nil {
		panic(err.Error())
	}
	fmt.Printf("%#v\n", result)
}

輸出:

main.Person{Name:"Tim", Age:31, Gender:"male"}

嵌入式結構

mapstructure 允許我們壓縮多個嵌入式結構,并通過 squash 標簽進行處理。

func embeddedStructDecode() {
	// 使用 squash 標簽允許壓縮多個嵌入式結構。通過創(chuàng)建多種類型的復合結構并對其進行解碼來演示此功能。
	type Family struct {
		LastName string
	}
	type Location struct {
		City string
	}
	type Person struct {
		Family    `mapstructure:",squash"`
		Location  `mapstructure:",squash"`
		FirstName string
	}

	input := map[string]interface{}{
		"FirstName": "Tim",
		"LastName":  "Liu",
		"City":      "China, Guangdong",
	}

	var result Person
	err := mapstructure.Decode(input, &result)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%s %s, %s\n", result.FirstName, result.LastName, result.City)
}

輸出:

Tim Liu, China, Guangdong

在這個例子中, Person 里面有著 Location 和 Family 的嵌入式結構體,通過 squash 標簽進行壓縮,從而達到平鋪的作用。

元數據

func metadataDecode() {
	type Person struct {
		Name   string
		Age    int
		Gender string
	}

	// 此輸入可以來自任何地方,但通常來自諸如解碼 JSON 之類的東西,我們最初不太確定結構。
	input := map[string]interface{}{
		"name":  "Tim",
		"age":   31,
		"email": "one@gmail.com",
	}

	// 對于元數據,我們制作了一個更高級的 DecoderConfig,以便我們可以更細致地配置所使用的解碼器。在這種情況下,我們只是告訴解碼器我們想要跟蹤元數據。
	var md mapstructure.Metadata
	var result Person
	config := &mapstructure.DecoderConfig{
		Metadata: &md,
		Result:   &result,
	}

	decoder, err := mapstructure.NewDecoder(config)
	if err != nil {
		panic(err)
	}

	if err = decoder.Decode(input); err != nil {
		panic(err)
	}

	fmt.Printf("value: %#v, keys: %#v, Unused keys: %#v, Unset keys: %#v\n", result, md.Keys, md.Unused, md.Unset)
}

輸出:

value: main.Person{Name:"Tim", Age:31, Gender:""}, keys: []string{"Name", "Age"}, Unused keys: []string{"email"}, Unset keys: []string{"Gender"}

從這個例子我們可以看出,使用 Metadata 可以記錄我們結構體以及 map[string]interface{} 的差異,相同的部分會正確映射到對應的字段中,而差異則使用了 Unused 和 Unset 來表達。

Unused:map 中有著結構體所沒有的字段。
Unset:結構體中有著 map 中所沒有的字段。

避免空值的映射

這里的使用其實和內置的 json 庫使用方式是一樣的,都是借助 omitempty 標簽來解決。

func omitemptyDecode() {
	// 添加 omitempty 注釋以避免空值的映射鍵
	type Family struct {
		LastName string
	}
	type Location struct {
		City string
	}
	type Person struct {
		*Family   `mapstructure:",omitempty"`
		*Location `mapstructure:",omitempty"`
		Age       int
		FirstName string
	}

	result := &map[string]interface{}{}
	input := Person{FirstName: "Somebody"}
	err := mapstructure.Decode(input, &result)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%+v\n", result)
}

輸出:

&map[Age:0 FirstName:Somebody]

這里我們可以看到 *Family 和 *Location 都被設置了 omitempty,所以在解析過程中會忽略掉空值。而 Age 沒有設置,并且 input 中沒有對應的 value,所以在解析中使用對應類型的零值來表達,而 int 類型的零值就是 0。

剩余字段

func remainDataDecode() {
	type Person struct {
		Name  string
		Age   int
		Other map[string]interface{} `mapstructure:",remain"`
	}

	input := map[string]interface{}{
		"name":   "Tim",
		"age":    31,
		"email":  "one@gmail.com",
		"gender": "male",
	}

	var result Person
	err := mapstructure.Decode(input, &result)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%#v\n", result)
}

輸出:

main.Person{Name:"Tim", Age:31, Other:map[string]interface {}{"email":"one@gmail.com", "gender":"male"}}

代碼可以看到 Other 字段被設置了 remain,這意味著 input 中沒有正確映射的字段都會被放到 Other 中,從輸出可以看到,email 和 gender 已經被正確的放到 Other 中了。

自定義標簽

func tagDecode() {
	// 請注意,結構類型中定義的 mapstructure 標簽可以指示將值映射到哪些字段。
	type Person struct {
		Name string `mapstructure:"person_name"`
		Age  int    `mapstructure:"person_age"`
	}

	input := map[string]interface{}{
		"person_name": "Tim",
		"person_age":  31,
	}

	var result Person
	err := mapstructure.Decode(input, &result)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%#v\n", result)
}

輸出:

main.Person{Name:"Tim", Age:31}

在 Person 結構中,我們將 person_name 和 person_age 分別映射到 Name 和 Age 中,從而達到在不破壞結構的基礎上,去正確的解析。

弱類型解析

正如前面所說,mapstructure 提供了類似 PHP 解析弱類型結構的方法。

func weaklyTypedInputDecode() {
	type Person struct {
		Name   string
		Age    int
		Emails []string
	}

	// 此輸入可以來自任何地方,但通常來自諸如解碼 JSON 之類的東西,由 PHP 等弱類型語言生成。
	input := map[string]interface{}{
		"name":   123,  // number => string
		"age":    "31", // string => number
		"emails": map[string]interface{}{}, // empty map => empty array
	}

	var result Person
	config := &mapstructure.DecoderConfig{
		WeaklyTypedInput: true,
		Result:           &result,
	}

	decoder, err := mapstructure.NewDecoder(config)
	if err != nil {
		panic(err)
	}

	err = decoder.Decode(input)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%#v\n", result)
}

輸出:

main.Person{Name:"123", Age:31, Emails:[]string{}}

從代碼可以看到,input 中的 name、age 和 Person 結構體中的 Name、Age 類型不一致,而 email 更是離譜,一個字符串數組,一個是 map。
但是我們通過自定義 DecoderConfig,將 WeaklyTypedInput 設置成 true 之后,mapstructure 很容易幫助我們解決這類弱類型的解析問題。
但是也不是所有問題都能解決,通過源碼我們可以知道有如下限制:

//   - bools to string (true = "1", false = "0")
//   - numbers to string (base 10)
//   - bools to int/uint (true = 1, false = 0)
//   - strings to int/uint (base implied by prefix)
//   - int to bool (true if value != 0)
//   - string to bool (accepts: 1, t, T, TRUE, true, True, 0, f, F,
//     FALSE, false, False. Anything else is an error)
//   - empty array = empty map and vice versa
//   - negative numbers to overflowed uint values (base 10)
//   - slice of maps to a merged map
//   - single values are converted to slices if required. Each
//     element is weakly decoded. For example: "4" can become []int{4}
//     if the target type is an int slice.

大家使用這種弱類型解析的時候也需要注意。

錯誤處理

mapstructure 錯誤提示非常的友好,下面我們來看看遇到錯誤時,它是怎么提示的。

func decodeErrorHandle() {
	type Person struct {
		Name   string
		Age    int
		Emails []string
		Extra  map[string]string
	}

	input := map[string]interface{}{
		"name":   123,
		"age":    "bad value",
		"emails": []int{1, 2, 3},
	}

	var result Person
	err := mapstructure.Decode(input, &result)
	if err != nil {
		fmt.Println(err.Error())
	}
}

輸出:

5 error(s) decoding:

* 'Age' expected type 'int', got unconvertible type 'string', value: 'bad value'
* 'Emails[0]' expected type 'string', got unconvertible type 'int', value: '1'
* 'Emails[1]' expected type 'string', got unconvertible type 'int', value: '2'
* 'Emails[2]' expected type 'string', got unconvertible type 'int', value: '3'
* 'Name' expected type 'string', got unconvertible type 'int', value: '123'

這里的錯誤提示會告訴我們每個字段,字段里的值應該需要怎么表達,我們可以通過這些錯誤提示,比較快的去修復問題。

總結

從上面這些例子看看到 mapstructure 的強大之處,很好的幫我們解決了實實在在的問題,也在節(jié)省我們的開發(fā)成本。
但是從源碼來看,內部使用了大量的反射,這可能會對一些特殊場景帶來性能隱患。所以大家在使用的時候,一定要充分考慮產品邏輯以及場景。
以下貼一小段刪減過的源碼:

// Decode decodes the given raw interface to the target pointer specified
// by the configuration.
func (d *Decoder) Decode(input interface{}) error {
	return d.decode("", input, reflect.ValueOf(d.config.Result).Elem())
}

// Decodes an unknown data type into a specific reflection value.
func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error {
	....

	var err error
	outputKind := getKind(outVal)
	addMetaKey := true
	switch outputKind {
	case reflect.Bool:
		err = d.decodeBool(name, input, outVal)
	case reflect.Interface:
		err = d.decodeBasic(name, input, outVal)
	case reflect.String:
		err = d.decodeString(name, input, outVal)
	case reflect.Int:
		err = d.decodeInt(name, input, outVal)
	case reflect.Uint:
		err = d.decodeUint(name, input, outVal)
	case reflect.Float32:
		err = d.decodeFloat(name, input, outVal)
	case reflect.Struct:
		err = d.decodeStruct(name, input, outVal)
	case reflect.Map:
		err = d.decodeMap(name, input, outVal)
	case reflect.Ptr:
		addMetaKey, err = d.decodePtr(name, input, outVal)
	case reflect.Slice:
		err = d.decodeSlice(name, input, outVal)
	case reflect.Array:
		err = d.decodeArray(name, input, outVal)
	case reflect.Func:
		err = d.decodeFunc(name, input, outVal)
	default:
		// If we reached this point then we weren't able to decode it
		return fmt.Errorf("%s: unsupported type: %s", name, outputKind)
	}

	// If we reached here, then we successfully decoded SOMETHING, so
	// mark the key as used if we're tracking metainput.
	if addMetaKey && d.config.Metadata != nil && name != "" {
		d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
	}

	return err
}

到此這篇關于go使用mapstructure解析json的實現實例的文章就介紹到這了,更多相關go 解析 json內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關文章

  • Ubuntu下安裝Go語言開發(fā)環(huán)境及編輯器的相關配置

    Ubuntu下安裝Go語言開發(fā)環(huán)境及編輯器的相關配置

    這篇文章主要介紹了Ubuntu下安裝Go語言開發(fā)環(huán)境及編輯器的相關配置,編輯器方面介紹了包括Vim和Eclipse,需要的朋友可以參考下
    2016-02-02
  • 基于Go語言開發(fā)一個編解碼工具

    基于Go語言開發(fā)一個編解碼工具

    這篇文章主要為大家詳細介紹了如何基于Go語言開發(fā)一個編解碼工具,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的小伙伴可以跟隨小編一起了解一下
    2025-03-03
  • Go語言中獲取IP地址的方法詳解

    Go語言中獲取IP地址的方法詳解

    這篇文章主要為大家詳細介紹了Go語言中獲取IP地址的相關方法,文中的示例代碼講解詳細,具有一定的學習價值,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-11-11
  • GO語言協(xié)程互斥鎖Mutex和讀寫鎖RWMutex用法實例詳解

    GO語言協(xié)程互斥鎖Mutex和讀寫鎖RWMutex用法實例詳解

    這篇文章主要介紹了GO語言協(xié)程互斥鎖Mutex和讀寫鎖RWMutex用法詳解,需要的朋友可以參考下
    2022-04-04
  • 使用Go語言實現一個簡單的詞頻分析系統(tǒng)

    使用Go語言實現一個簡單的詞頻分析系統(tǒng)

    在數據分析和文本挖掘中,詞頻統(tǒng)計(Word Frequency Analysis) 是最基礎也是最常用的技術之一,本文將帶你用 Go 語言實現一個簡易的 詞頻分析系統(tǒng),感興趣的小伙伴可以了解下
    2025-09-09
  • 談談對Golang IO讀寫的困惑

    談談對Golang IO讀寫的困惑

    這篇文章主要介紹了談談對Golang IO讀寫的困惑,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-08-08
  • 一文帶你熟悉Go語言中函數的使用

    一文帶你熟悉Go語言中函數的使用

    這篇文章主要和大家分享一下Go語言中的函數的使用,文中的示例代碼講解詳細,對我們學習Go語言有一定的幫助,需要的小伙伴可以參考一下
    2022-11-11
  • go-zero使用goctl生成mongodb的操作使用方法

    go-zero使用goctl生成mongodb的操作使用方法

    mongodb是一種高性能、開源、文檔型的nosql數據庫,被廣泛應用于web應用、大數據以及云計算領域,goctl model 為 goctl 提供的數據庫模型代碼生成指令,目前支持 MySQL、PostgreSQL、Mongo 的代碼生成,本文給大家介紹了go-zero使用goctl生成mongodb的操作使用方法
    2024-06-06
  • golang讀取yaml配置文件的示例代碼

    golang讀取yaml配置文件的示例代碼

    在項目開發(fā)中,經常需要把一些配置文件常量提取到統(tǒng)一配置文件進行維護,go項目在開發(fā)中常常把需要維護的常量或者配置提取到y(tǒng)aml文件,所以本文主要來為大家介紹一下golang如何讀取yaml配置文件吧
    2023-11-11
  • go?sync?Waitgroup數據結構實現基本操作詳解

    go?sync?Waitgroup數據結構實現基本操作詳解

    這篇文章主要為大家介紹了go?sync?Waitgroup數據結構實現基本操作詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01

最新評論

高陵县| 武邑县| 囊谦县| 甘南县| 镇安县| 勐海县| 洪湖市| 泰顺县| 宁城县| 盐津县| 保德县| 眉山市| 东乌珠穆沁旗| 桃园县| 集安市| 灵宝市| 志丹县| 靖西县| 浪卡子县| 宁德市| 衡东县| 财经| 博客| 九龙城区| 舟山市| 启东市| 湖州市| 溧水县| 修水县| 白山市| 油尖旺区| 轮台县| 伽师县| 仲巴县| 合水县| 华亭县| 丰顺县| 奉贤区| 清镇市| 凌海市| 绿春县|