溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Golang結(jié)構(gòu)體映射mapstructure庫(kù)怎么使用

發(fā)布時(shí)間:2023-01-03 10:18:56 來(lái)源:億速云 閱讀:117 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹“Golang結(jié)構(gòu)體映射mapstructure庫(kù)怎么使用”,在日常操作中,相信很多人在Golang結(jié)構(gòu)體映射mapstructure庫(kù)怎么使用問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Golang結(jié)構(gòu)體映射mapstructure庫(kù)怎么使用”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

    在數(shù)據(jù)傳遞時(shí),需要先編解碼;常用的方式是JSON編解碼。但有時(shí)卻需要讀取部分字段后,才能知道具體類型,此時(shí)就可借助mapstructure庫(kù)了。

    mapstructure庫(kù)

    mapstructure可方便地實(shí)現(xiàn)map[string]interface{}struct間的轉(zhuǎn)換;使用前,需要先導(dǎo)入庫(kù):

    go get github.com/mitchellh/mapstructure

    字段標(biāo)簽

    默認(rèn)情況下,mapstructure使用字段的名稱做匹配映射(即在map中以字段名為鍵值查找字段值);注意匹配時(shí)是忽略大小寫(xiě)的。也可通過(guò)標(biāo)簽來(lái)設(shè)定字段映射名稱:

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

    內(nèi)嵌結(jié)構(gòu)

    go中結(jié)構(gòu)體是可以任意嵌套的;嵌套后即認(rèn)為擁有對(duì)應(yīng)的字段。但是,默認(rèn)情況下mapstructure只處理當(dāng)前結(jié)構(gòu)定義的字段,若要自動(dòng)處理內(nèi)嵌字段需要添加標(biāo)簽squash

    type Student struct {
      Person `mapstructure:",squash"`
      Age int 
    }

    未映射字段

    若源數(shù)據(jù)中有未映射的值(即結(jié)構(gòu)體中無(wú)對(duì)應(yīng)的字段),mapstructure默認(rèn)會(huì)忽略它??梢栽诮Y(jié)構(gòu)體中定義一個(gè)特殊字段(類型為map[string]interface{},且標(biāo)簽要設(shè)置為mapstructure:",remain"),來(lái)存放所有未能映射的字段中。

    type Student struct {
      Name  string
      Age   int
      Other map[string]interface{} `mapstructure:",remain"`
    }

    Metadata

    mapstructure中可以使用Metadata收集一些解碼時(shí)會(huì)產(chǎn)生的有用信息。

    // mapstructure.go
    type Metadata struct {
      Keys   []string  // 解碼成功的鍵
      Unused []string  // 源數(shù)據(jù)中存在,但目標(biāo)結(jié)構(gòu)中不存在的鍵
      Unset  []string  // 未設(shè)定的(源數(shù)據(jù)中缺失的)鍵
    }

    為了獲取這些信息,需要使用DecodeMetadata來(lái)解碼:

      var metadata mapstructure.Metadata
      err := mapstructure.DecodeMetadata(m, &p, &metadata)

    弱類型輸入

    有時(shí)候,并不想對(duì)結(jié)構(gòu)體字段類型和map[string]interface{}的對(duì)應(yīng)鍵值做強(qiáng)類型一致的校驗(yàn)。這時(shí)可以使用WeakDecode/WeakDecodeMetadata方法,它們會(huì)嘗試做類型轉(zhuǎn)換:

    • 布爾轉(zhuǎn)字符串:true = “1”, false = “0”;

    • 布爾轉(zhuǎn)數(shù)字:true = 1, false = 0;

    • 數(shù)字轉(zhuǎn)布爾:true if value != 0;

    • 字符串轉(zhuǎn)布爾:可接受,

    • 真:1, t, T, TRUE, true, True

    • 假:0, f, F, FALSE, false, False

    • 數(shù)字轉(zhuǎn)字符串:自動(dòng)base10轉(zhuǎn)換;

    • 負(fù)數(shù)轉(zhuǎn)為無(wú)符號(hào)數(shù)(上溢);

    • 字符串轉(zhuǎn)數(shù)字:根據(jù)前綴(如0x等)轉(zhuǎn)換;

    • 空數(shù)組與空map間互轉(zhuǎn);

    • 單個(gè)值轉(zhuǎn)為切片;

    逆向轉(zhuǎn)換

    除將map轉(zhuǎn)換為結(jié)構(gòu)體外,mapstructure也可以將結(jié)構(gòu)體反向解碼為map[string]interface{}。在反向解碼時(shí),我們可以為某些字段設(shè)置mapstructure:“,omitempty”,當(dāng)這些字段為默認(rèn)值時(shí),就不會(huì)出現(xiàn)在map中:

      p := &Student{
        Name: "Mike",
        Age:  12,
      }
      var m map[string]interface{}
      mapstructure.Decode(p, &m)

    解碼器

    mapstructure提供了解碼器(Decoder),可靈活方便地控制解碼:

    type DecoderConfig struct {
        // 若設(shè)定,則在任何解碼或類型轉(zhuǎn)換(設(shè)定了WeaklyTypedInput)前調(diào)用;對(duì)于設(shè)定了squash的內(nèi)嵌字段,整體調(diào)用一次;若返回錯(cuò)誤,則整個(gè)解碼失敗
        DecodeHook DecodeHookFunc
        // 若設(shè)定,則源數(shù)據(jù)中存在未使用字段時(shí),報(bào)錯(cuò)
        ErrorUnused bool
        // 若設(shè)定,則有字段未設(shè)定時(shí),報(bào)錯(cuò)
        ErrorUnset bool
        // 若設(shè)定,則在設(shè)定字段前先清空(對(duì)于map等類型會(huì)先清理掉舊數(shù)據(jù))
        ZeroFields bool
        // 若設(shè)定,支持若類型間的轉(zhuǎn)換
        WeaklyTypedInput bool
        // Squash will squash embedded structs. 
        Squash bool
        // Metadata is the struct that will contain extra metadata about
        // the decoding. If this is nil, then no metadata will be tracked.
        Metadata *Metadata
        // Result is a pointer to the struct that will contain the decoded
        // value.
        Result interface{}
        // The tag name that mapstructure reads for field names. This
        // defaults to "mapstructure"
        TagName string
        // IgnoreUntaggedFields ignores all struct fields without explicit
        // TagName, comparable to `mapstructure:"-"` as default behaviour.
        IgnoreUntaggedFields bool
        // MatchName is the function used to match the map key to the struct
        // field name or tag. Defaults to `strings.EqualFold`. This can be used
        // to implement case-sensitive tag values, support snake casing, etc.
        MatchName func(mapKey, fieldName string) bool
    }

    一個(gè)支持弱類型轉(zhuǎn)換的示例:要獲取的結(jié)果放到config的result中

        Name string
        Age  int
    }
    func decoderConfig() {
        m := map[string]interface{}{
            "name": 123,
            "age":  "12",
            "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 {
            log.Printf("Result: %#v", p)
            log.Printf("keys:%#v, unused:%#v\n", metadata.Keys, metadata.Unused)
        } else {
            log.Println("decode fail:", err)
        }
    }

    示例

    通過(guò)一個(gè)messageData結(jié)構(gòu),action會(huì)指示最終的data類型。接收到數(shù)據(jù)后,先解析出atcion,再根據(jù)action轉(zhuǎn)換為真實(shí)的類型。

    因time.Time是一個(gè)結(jié)構(gòu)體(json序列化時(shí)會(huì)轉(zhuǎn)換為時(shí)間字符串),mapstructure無(wú)法正確處理,所以推薦使用時(shí)間戳。

    為了能正確解析內(nèi)嵌的DataBasic,需要標(biāo)記為squash。

    import "github.com/mitchellh/mapstructure"
    type DataBasic struct {
        DataId     string `json:"dataId"`
        UpdateTime int64  `json:"updateTime"`
    }
    type AddedData struct {
        DataBasic `mapstructure:",squash"`
        Tag string `json:"tag"`
        AddParams map[string]any `json:"addParams"`
    }
    type messageData struct {
        Action    int    `json:"action"`
        SeqId     uint64 `json:"seqId"`
        Data      any    `json:"data"`
    }
    func decodeData() {
        add := &AddedData{
            DataBasic: DataBasic{
                DataId:     "a2",
                UpdateTime: time.Now().UnixMilli(),
            },
            Tag: "tag",
            AddParams:  map[string]any{"dataId": "c2", "otherId": "t2"},
        }
        data := &messageData{
            Action: 1,
            Data:   add,
        }
        js, err := json.Marshal(data)
        if err != nil {
            log.Printf("marshal fail: %v", err)
            return
        }
        got := &messageData{}
        err = json.Unmarshal(js, got)
        if err != nil {
            log.Printf("unmarshal fail: %v", err)
            return
        }
        param := new(AddedData)
        err = mapstructure.Decode(got.Data, param)
        if err != nil {
            log.Printf("unmarshal fail: %v", err)
            return
        }
        log.Printf("param: %+v", param)
    }

    到此,關(guān)于“Golang結(jié)構(gòu)體映射mapstructure庫(kù)怎么使用”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    go
    AI