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

golang mapstructure庫的具體使用

 更新時間:2023年09月12日 16:20:19   作者:梅塢茶坊  
mapstructure用于將通用的map[string]interface{}解碼到對應的 Go 結構體中,或者執(zhí)行相反的操作,本文主要介紹了golang mapstructure庫的具體使用,感興趣的可以了解一下

簡介

mapstructure用于將通用的map[string]interface{}解碼到對應的 Go 結構體中,或者執(zhí)行相反的操作。很多時候,解析來自多種源頭的數(shù)據(jù)流時,我們一般事先并不知道他們對應的具體類型。只有讀取到一些字段之后才能做出判斷。這時,我們可以先使用標準的encoding/json庫將數(shù)據(jù)解碼為map[string]interface{}類型,然后根據(jù)標識字段利用mapstructure庫轉為相應的 Go 結構體以便使用。

快速使用

本文代碼采用 Go Modules。

首先創(chuàng)建目錄并初始化:

$ mkdir mapstructure && cd mapstructure
$ go mod init github.com/darjun/go-daily-lib/mapstructure

下載mapstructure庫:

$ go get github.com/mitchellh/mapstructure

使用:

package main
import (
  "encoding/json"
  "fmt"
  "log"
  "github.com/mitchellh/mapstructure"
)
type Person struct {
  Name string
  Age  int
  Job  string
}
type Cat struct {
  Name  string
  Age   int
  Breed string
}
func main() {
  datas := []string{`
    { 
      "type": "person",
      "name":"dj",
      "age":18,
      "job": "programmer"
    }
  `,
    `
    {
      "type": "cat",
      "name": "kitty",
      "age": 1,
      "breed": "Ragdoll"
    }
  `,
  }
  for _, data := range datas {
    var m map[string]interface{}
    err := json.Unmarshal([]byte(data), &m)
    if err != nil {
      log.Fatal(err)
    }
    switch m["type"].(string) {
    case "person":
      var p Person
      mapstructure.Decode(m, &p)
      fmt.Println("person", p)
    case "cat":
      var cat Cat
      mapstructure.Decode(m, &cat)
      fmt.Println("cat", cat)
    }
  }
}

運行結果:

$ go run main.go
person {dj 18 programmer}
cat {kitty 1 Ragdoll}

我們定義了兩個結構體PersonCat,他們的字段有些許不同?,F(xiàn)在,我們約定通信的 JSON 串中有一個type字段。當type的值為person時,該 JSON 串表示的是Person類型的數(shù)據(jù)。當type的值為cat時,該 JSON 串表示的是Cat類型的數(shù)據(jù)。

上面代碼中,我們先用json.Unmarshal將字節(jié)流解碼為map[string]interface{}類型。然后讀取里面的type字段。根據(jù)type字段的值,再使用mapstructure.Decode將該 JSON 串分別解碼為PersonCat類型的值,并輸出。

實際上,Google Protobuf 通常也使用這種方式。在協(xié)議中添加消息 ID 或全限定消息名。接收方收到數(shù)據(jù)后,先讀取協(xié)議 ID 或全限定消息名。然后調用 Protobuf 的解碼方法將其解碼為對應的Message結構。從這個角度來看,mapstructure也可以用于網絡消息解碼,如果你不考慮性能的話?。

字段標簽

默認情況下,mapstructure使用結構體中字段的名稱做這個映射,例如我們的結構體有一個Name字段,mapstructure解碼時會在map[string]interface{}中查找鍵名name。注意,這里的name是大小寫不敏感的!

type Person struct {
  Name string
}

當然,我們也可以指定映射的字段名。為了做到這一點,我們需要為字段設置mapstructure標簽。例如下面使用username代替上例中的name

type Person struct {
  Name string `mapstructure:"username"`
}

看示例:

type Person struct {
  Name string `mapstructure:"username"`
  Age  int
  Job  string
}
type Cat struct {
  Name  string
  Age   int
  Breed string
}
func main() {
  datas := []string{`
    { 
      "type": "person",
      "username":"dj",
      "age":18,
      "job": "programmer"
    }
  `,
    `
    {
      "type": "cat",
      "name": "kitty",
      "Age": 1,
      "breed": "Ragdoll"
    }
  `,
    `
    {
      "type": "cat",
      "Name": "rooooose",
      "age": 2,
      "breed": "shorthair"
    }
  `,
  }
  for _, data := range datas {
    var m map[string]interface{}
    err := json.Unmarshal([]byte(data), &m)
    if err != nil {
      log.Fatal(err)
    }
    switch m["type"].(string) {
    case "person":
      var p Person
      mapstructure.Decode(m, &p)
      fmt.Println("person", p)
    case "cat":
      var cat Cat
      mapstructure.Decode(m, &cat)
      fmt.Println("cat", cat)
    }
  }
}

上面代碼中,我們使用標簽mapstructure:"username"PersonName字段映射為username,在 JSON 串中我們需要設置username才能正確解析。另外,注意到,我們將第二個 JSON 串中的Age和第三個 JSON 串中的Name首字母大寫了,但是并沒有影響解碼結果。mapstructure處理字段映射是大小寫不敏感的。

內嵌結構

結構體可以任意嵌套,嵌套的結構被認為是擁有該結構體名字的另一個字段。例如,下面兩種Friend的定義方式對于mapstructure是一樣的:

type Person struct {
  Name string
}
// 方式一
type Friend struct {
  Person
}
// 方式二
type Friend struct {
  Person Person
}

為了正確解碼,Person結構的數(shù)據(jù)要在person鍵下:

map[string]interface{} {
  "person": map[string]interface{}{"name": "dj"},
}

我們也可以設置mapstructure:",squash"將該結構體的字段提到父結構中:

type Friend struct {
  Person `mapstructure:",squash"`
}

這樣只需要這樣的 JSON 串,無效嵌套person鍵:

map[string]interface{}{
  "name": "dj",
}

看示例:

type Person struct {
  Name string
}
type Friend1 struct {
  Person
}
type Friend2 struct {
  Person `mapstructure:",squash"`
}
func main() {
  datas := []string{`
    { 
      "type": "friend1",
      "person": {
        "name":"dj"
      }
    }
  `,
    `
    {
      "type": "friend2",
      "name": "dj2"
    }
  `,
  }
  for _, data := range datas {
    var m map[string]interface{}
    err := json.Unmarshal([]byte(data), &m)
    if err != nil {
      log.Fatal(err)
    }
    switch m["type"].(string) {
    case "friend1":
      var f1 Friend1
      mapstructure.Decode(m, &f1)
      fmt.Println("friend1", f1)
    case "friend2":
      var f2 Friend2
      mapstructure.Decode(m, &f2)
      fmt.Println("friend2", f2)
    }
  }
}

注意對比Friend1Friend2使用的 JSON 串的不同。

另外需要注意一點,如果父結構體中有同名的字段,那么mapstructure會將JSON 中對應的值同時設置到這兩個字段中,即這兩個字段有相同的值。

未映射的值

如果源數(shù)據(jù)中有未映射的值(即結構體中無對應的字段),mapstructure默認會忽略它。

我們可以在結構體中定義一個字段,為其設置mapstructure:",remain"標簽。這樣未映射的值就會添加到這個字段中。注意,這個字段的類型只能為map[string]interface{}map[interface{}]interface{}。

看示例:

type Person struct {
  Name  string
  Age   int
  Job   string
  Other map[string]interface{} `mapstructure:",remain"`
}
func main() {
  data := `
    { 
      "name": "dj",
      "age":18,
      "job":"programmer",
      "height":"1.8m",
      "handsome": true
    }
  `
  var m map[string]interface{}
  err := json.Unmarshal([]byte(data), &m)
  if err != nil {
    log.Fatal(err)
  }
  var p Person
  mapstructure.Decode(m, &p)
  fmt.Println("other", p.Other)
}

上面代碼中,我們?yōu)榻Y構體定義了一個Other字段,用于保存未映射的鍵值。輸出結果:

other map[handsome:true height:1.8m]

逆向轉換

前面我們都是將map[string]interface{}解碼到 Go 結構體中。mapstructure當然也可以將 Go 結構體反向解碼為map[string]interface{}。在反向解碼時,我們可以為某些字段設置mapstructure:",omitempty"。這樣當這些字段為默認值時,就不會出現(xiàn)在結構的map[string]interface{}中:

type Person struct {
  Name string
  Age  int
  Job  string `mapstructure:",omitempty"`
}
func main() {
  p := &Person{
    Name: "dj",
    Age:  18,
  }
  var m map[string]interface{}
  mapstructure.Decode(p, &m)
  data, _ := json.Marshal(m)
  fmt.Println(string(data))
}

上面代碼中,我們?yōu)?code>Job字段設置了mapstructure:",omitempty",且對象pJob字段未設置。運行結果:

$ go run main.go 
{"Age":18,"Name":"dj"}

Metadata

解碼時會產生一些有用的信息,mapstructure可以使用Metadata收集這些信息。Metadata結構如下:

// mapstructure.go
type Metadata struct {
  Keys   []string
  Unused []string
}

Metadata只有兩個導出字段:

  • Keys:解碼成功的鍵名;
  • Unused:在源數(shù)據(jù)中存在,但是目標結構中不存在的鍵名。

為了收集這些數(shù)據(jù),我們需要使用DecodeMetadata來代替Decode方法:

type Person struct {
  Name string
  Age  int
}
func main() {
  m := map[string]interface{}{
    "name": "dj",
    "age":  18,
    "job":  "programmer",
  }
  var p Person
  var metadata mapstructure.Metadata
  mapstructure.DecodeMetadata(m, &p, &metadata)
  fmt.Printf("keys:%#v unused:%#v\n", metadata.Keys, metadata.Unused)
}

先定義一個Metadata結構,傳入DecodeMetadata收集解碼的信息。運行結果:

$ go run main.go 
keys:[]string{"Name", "Age"} unused:[]string{"job"}

錯誤處理

mapstructure執(zhí)行轉換的過程中不可避免地會產生錯誤,例如 JSON 中某個鍵的類型與對應 Go 結構體中的字段類型不一致。Decode/DecodeMetadata會返回這些錯誤:

type Person struct {
  Name   string
  Age    int
  Emails []string
}
func main() {
  m := map[string]interface{}{
    "name":   123,
    "age":    "bad value",
    "emails": []int{1, 2, 3},
  }
  var p Person
  err := mapstructure.Decode(m, &p)
  if err != nil {
    fmt.Println(err.Error())
  }
}

上面代碼中,結構體中Person中字段Namestring類型,但輸入中nameint類型;字段Ageint類型,但輸入中agestring類型;字段Emails[]string類型,但輸入中emails[]int類型。故Decode返回錯誤。運行結果:

$ go run main.go 
5 error(s) decoding:
* 'Age' expected type 'int', got unconvertible type 'string'
* 'Emails[0]' expected type 'string', got unconvertible type 'int'
* 'Emails[1]' expected type 'string', got unconvertible type 'int'
* 'Emails[2]' expected type 'string', got unconvertible type 'int'
* 'Name' expected type 'string', got unconvertible type 'int'

從錯誤信息中很容易看出哪里出錯了。

弱類型輸入

有時候,我們并不想對結構體字段類型和map[string]interface{}的對應鍵值做強類型一致的校驗。這時可以使用WeakDecode/WeakDecodeMetadata方法,它們會嘗試做類型轉換:

type Person struct {
  Name   string
  Age    int
  Emails []string
}
func main() {
  m := map[string]interface{}{
    "name":   123,
    "age":    "18",
    "emails": []int{1, 2, 3},
  }
  var p Person
  err := mapstructure.WeakDecode(m, &p)
  if err == nil {
    fmt.Println("person:", p)
  } else {
    fmt.Println(err.Error())
  }
}

雖然鍵name對應的值123int類型,但是在WeakDecode中會將其轉換為string類型以匹配Person.Name字段的類型。同樣的,age的值"18"string類型,在WeakDecode中會將其轉換為int類型以匹配Person.Age字段的類型。
需要注意一點,如果類型轉換失敗了,WeakDecode同樣會返回錯誤。例如將上例中的age設置為"bad value",它就不能轉為int類型,故而返回錯誤。

解碼器

除了上面介紹的方法外,mapstructure還提供了更靈活的解碼器(Decoder)??梢酝ㄟ^配置DecoderConfig實現(xiàn)上面介紹的任何功能:

// mapstructure.go
type DecoderConfig struct {
    ErrorUnused       bool
    ZeroFields        bool
    WeaklyTypedInput  bool
    Metadata          *Metadata
    Result            interface{}
    TagName           string
}

各個字段含義如下:

  • ErrorUnused:為true時,如果輸入中的鍵值沒有與之對應的字段就返回錯誤;
  • ZeroFields:為true時,在Decode前清空目標map。為false時,則執(zhí)行的是map的合并。用在structmap的轉換中;
  • WeaklyTypedInput:實現(xiàn)WeakDecode/WeakDecodeMetadata的功能;
  • Metadata:不為nil時,收集Metadata數(shù)據(jù);
  • Result:為結果對象,在mapstruct的轉換中,Resultstruct類型。在structmap的轉換中,Resultmap類型;
  • TagName:默認使用mapstructure作為結構體的標簽名,可以通過該字段設置。

看示例:

type Person struct {
  Name string
  Age  int
}
func main() {
  m := map[string]interface{}{
    "name": 123,
    "age":  "18",
    "job":  "programmer",
  }
  var p Person
  var metadata mapstructure.Metadata
  decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
    WeaklyTypedInput: true,
    Result:           &p,
    Metadata:         &metadata,
  })
  if err != nil {
    log.Fatal(err)
  }
  err = decoder.Decode(m)
  if err == nil {
    fmt.Println("person:", p)
    fmt.Printf("keys:%#v, unused:%#v\n", metadata.Keys, metadata.Unused)
  } else {
    fmt.Println(err.Error())
  }
}

這里用Decoder的方式實現(xiàn)了前面弱類型輸入小節(jié)中的示例代碼。實際上WeakDecode內部就是通過這種方式實現(xiàn)的,下面是WeakDecode的源碼:

// mapstructure.go
func WeakDecode(input, output interface{}) error {
  config := &DecoderConfig{
    Metadata:         nil,
    Result:           output,
    WeaklyTypedInput: true,
  }
  decoder, err := NewDecoder(config)
  if err != nil {
    return err
  }
  return decoder.Decode(input)
}

再實際上,Decode/DecodeMetadata/WeakDecodeMetadata內部都是先設置DecoderConfig的對應字段,然后創(chuàng)建Decoder對象,最后調用其Decode方法實現(xiàn)的。

總結

mapstructure實現(xiàn)優(yōu)雅,功能豐富,代碼結構清晰,非常推薦一看!

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

相關文章

  • Go語言sync.Pool對象池使用場景基本示例

    Go語言sync.Pool對象池使用場景基本示例

    這篇文章主要為大家介紹了Go語言sync.Pool對象池使用場景的基本示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • 淺談Golang的方法傳遞值應該注意的地方

    淺談Golang的方法傳遞值應該注意的地方

    這篇文章主要介紹了淺談Golang的方法傳遞值應該注意的地方,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go語言實現(xiàn)優(yōu)雅關機和重啟的示例詳解

    Go語言實現(xiàn)優(yōu)雅關機和重啟的示例詳解

    優(yōu)雅的關機是指在關閉服務之前,先讓服務處理完當前正在處理的請求,然后再關閉服務,本文主要為大家詳細介紹了如何使用Go語言實現(xiàn)優(yōu)雅關機和重啟,感興趣的小伙伴可以參考一下
    2025-04-04
  • 詳解Golang中Context的原理和使用技巧

    詳解Golang中Context的原理和使用技巧

    Golang?的?Context?包,中文可以稱之為“上下文”,是用來在?goroutine?協(xié)程之間進行上下文信息傳遞的,這些上下文信息包括?kv?數(shù)據(jù)、取消信號、超時時間、截止時間等。本文主要介紹了Context的原理和使用技巧,希望對大家有所幫助
    2022-11-11
  • GOLang?IO接口與工具使用方法講解

    GOLang?IO接口與工具使用方法講解

    這篇文章主要介紹了GOLang?IO接口與工具使用方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧
    2023-03-03
  • 一文帶你掌握Golang Interface原理和使用技巧

    一文帶你掌握Golang Interface原理和使用技巧

    Golang 中的 interface 是一種非常重要的特性,可以讓我們寫出更加靈活的代碼。在本篇文章中,我們將深入探討 Golang 中interface 的原理和使用技巧,感興趣的可以了解一下
    2023-04-04
  • linux中用shell快速安裝配置Go語言的開發(fā)環(huán)境

    linux中用shell快速安裝配置Go語言的開發(fā)環(huán)境

    相信每位開發(fā)者都知道選擇一門開發(fā)語言,免不了需要安裝配置開發(fā)環(huán)境,所以這篇文章給大家分享了linux下使用shell一鍵安裝配置GO語言開發(fā)環(huán)境的方法,有需要的朋友們可以參考借鑒,下面來一起看看吧。
    2016-10-10
  • 深入了解Golang網絡編程Net包的使用

    深入了解Golang網絡編程Net包的使用

    net包主要是增加?context?控制,封裝了一些不同的連接類型以及DNS?查找等等,同時在有需要的地方引入?goroutine?提高處理效率。本文主要和大家分享下在Go中網絡編程的實現(xiàn),需要的可以參考一下
    2022-07-07
  • Golang實現(xiàn)事務型內存數(shù)據(jù)庫的方法詳解

    Golang實現(xiàn)事務型內存數(shù)據(jù)庫的方法詳解

    內存數(shù)據(jù)庫經我們經常用到,例如Redis,那么如何從零實現(xiàn)一個內存數(shù)據(jù)庫呢,本文旨在介紹如何使用Golang編寫一個KV內存數(shù)據(jù)庫MossDB
    2023-03-03
  • golang?鏈路追蹤的實現(xiàn)示例

    golang?鏈路追蹤的實現(xiàn)示例

    本文主要介紹了golang?鏈路追蹤的實現(xiàn)示例,包括調用鏈過長和接口響應慢的問題,具有一定的參考價值,感興趣的可以了解一下
    2025-03-03

最新評論