1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        技巧:Go 結(jié)構(gòu)體如何轉(zhuǎn)換成 map[string]interface{}

        共 5117字,需瀏覽 11分鐘

         ·

        2021-11-13 18:54

        本文介紹了Go語言中將結(jié)構(gòu)體轉(zhuǎn)成map[string]interface{}時你需要了解的“坑”,也有你需要知道的若干方法。

        我們在Go語言中通常使用結(jié)構(gòu)體來保存我們的數(shù)據(jù),例如要存儲用戶信息,我們可能會定義如下結(jié)構(gòu)體:

        // UserInfo 用戶信息type UserInfo struct {    Name string `json:"name"`    Age  int    `json:"age"`}
        u1 := UserInfo{Name: "q1mi", Age: 18}

        假設(shè)現(xiàn)在要將上面的u1轉(zhuǎn)換成map[string]interface{},該如何操作呢?

        結(jié)構(gòu)體轉(zhuǎn)map[string]interface{}

        JSON序列化方式

        這不是很簡單嗎?我用json序列化一下u1,再反序列化成map不就可以了么。說干就干,代碼如下:

        func main() {    u1 := UserInfo{Name: "q1mi", Age: 18}
        b, _ := json.Marshal(&u1) var m map[string]interface{} _ = json.Unmarshal(b, &m) for k, v := range m{ fmt.Printf("key:%v value:%v\n", k, v) }}

        輸出:

        key:name value:q1mikey:age value:18

        看起來沒什么問題,但其實這里是有一個“坑”的。那就是Go語言中的json包在序列化空接口存放的數(shù)字類型(整型、浮點型等)都會序列化成float64類型。

        也就是上面例子中m["age"]現(xiàn)在底層是一個float64了,不是個int了。我們來驗證下:

        func main() {    u1 := UserInfo{Name: "q1mi", Age: 18}
        b, _ := json.Marshal(&u1) var m map[string]interface{} _ = json.Unmarshal(b, &m) for k, v := range m{ fmt.Printf("key:%v value:%v value type:%T\n", k, v, v) }}

        輸出:

        key:name value:q1mi value type:stringkey:age value:18 value type:float64

        很顯然,出現(xiàn)了一個意料之外的行為,我們得想辦法規(guī)避掉。

        反射

        沒辦法就需要自己動手去實現(xiàn)了。這里使用反射遍歷結(jié)構(gòu)體字段的方式生成map, 具體代碼如下:

        // ToMap 結(jié)構(gòu)體轉(zhuǎn)為Map[string]interface{}func ToMap(in interface{}, tagName string) (map[string]interface{}, error){    out := make(map[string]interface{})
        v := reflect.ValueOf(in) if v.Kind() == reflect.Ptr { v = v.Elem() }
        if v.Kind() != reflect.Struct { // 非結(jié)構(gòu)體返回錯誤提示 return nil, fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v) }
        t := v.Type() // 遍歷結(jié)構(gòu)體字段 // 指定tagName值為map中key;字段值為map中value for i := 0; i < v.NumField(); i++ { fi := t.Field(i) if tagValue := fi.Tag.Get(tagName); tagValue != "" { out[tagValue] = v.Field(i).Interface() } } return out, nil}

        驗證一下:

        m2, _ := ToMap(&u1, "json")for k, v := range m2{    fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)}

        輸出:

        key:name value:q1mi value type:stringkey:age value:18 value type:int

        這一次map["age"]的類型就對了的。

        第三方庫structs

        除了自己使用反射實現(xiàn),Github上也有現(xiàn)成的輪子,例如第三方庫:https://github.com/fatih/structs。

        這個包使用的自定義結(jié)構(gòu)體tag是structs:

        // UserInfo 用戶信息type UserInfo struct {    Name string `json:"name" structs:"name"`    Age  int    `json:"age" structs:"age"`}

        用法很簡單:

        m3 := structs.Map(&u1)for k, v := range m3 {    fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)}

        structs這個包也有很多其他的使用示例,大家可以去查看文檔。但是需要注意的是目前這個庫已經(jīng)被作者設(shè)置為只讀了。

        嵌套結(jié)構(gòu)體轉(zhuǎn)map[string]interface{}

        structs本身是支持嵌套結(jié)構(gòu)體轉(zhuǎn)map[string]interface{}的,遇到結(jié)構(gòu)體嵌套它會轉(zhuǎn)換為map[string]interface{}嵌套map[string]interface{}的模式。

        我們定義一組嵌套的結(jié)構(gòu)體如下:

        // UserInfo 用戶信息type UserInfo struct {    Name string `json:"name" structs:"name"`    Age  int    `json:"age" structs:"age"`    Profile `json:"profile" structs:"profile"`}
        // Profile 配置信息type Profile struct { Hobby string `json:"hobby" structs:"hobby"`}

        聲明結(jié)構(gòu)體變量u1:

        u1 := UserInfo{Name: "q1mi", Age: 18, Profile: Profile{"雙色球"}}

        第三方庫structs

        轉(zhuǎn)換嵌套結(jié)構(gòu)體的代碼和上面其實是一樣的:

        m3 := structs.Map(&u1)for k, v := range m3 {    fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)}

        輸出結(jié)果:

        key:name value:q1mi value type:stringkey:age value:18 value type:intkey:profile value:map[hobby:雙色球] value type:map[string]interface {}

        從結(jié)果來看最后嵌套字段profilemap[string]interface {},屬于map嵌套map。

        使用反射轉(zhuǎn)成單層map

        如果我們想把嵌套的結(jié)構(gòu)體轉(zhuǎn)換成一個單層map該怎么做呢?

        我們只需要把上面反射的代碼稍微修改一下就可以了,修改后的代碼如下:

        // ToMap2 將結(jié)構(gòu)體轉(zhuǎn)為單層mapfunc ToMap2(in interface{}, tag string) (map[string]interface{}, error) {
        // 當(dāng)前函數(shù)只接收struct類型 v := reflect.ValueOf(in) if v.Kind() == reflect.Ptr { // 結(jié)構(gòu)體指針 v = v.Elem() } if v.Kind() != reflect.Struct { return nil, fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v) }
        out := make(map[string]interface{}, 8) queue := make([]interface{}, 0, 2) queue = append(queue, in)
        for len(queue) > 0 { v := reflect.ValueOf(queue[0]) if v.Kind() == reflect.Ptr { // 結(jié)構(gòu)體指針 v = v.Elem() } queue = queue[1:] t := v.Type() for i := 0; i < v.NumField(); i++ { vi := v.Field(i) if vi.Kind() == reflect.Ptr { // 內(nèi)嵌指針 vi = vi.Elem() if vi.Kind() == reflect.Struct { // 結(jié)構(gòu)體 queue = append(queue, vi.Interface()) } else { ti := t.Field(i) if tagValue := ti.Tag.Get(tag); tagValue != "" { // 存入map out[tagValue] = vi.Interface() } } break } if vi.Kind() == reflect.Struct { // 內(nèi)嵌結(jié)構(gòu)體 queue = append(queue, vi.Interface()) break } // 一般字段 ti := t.Field(i) if tagValue := ti.Tag.Get(tag); tagValue != "" { // 存入map out[tagValue] = vi.Interface() } } } return out, nil}

        測試一下:

        m4, _ := ToMap2(&u1, "json")for k, v := range m4 {    fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)}

        輸出:

        key:name value:q1mi value type:stringkey:age value:18 value type:intkey:hobby value:雙色球 value type:string

        這下我們就把嵌套的結(jié)構(gòu)體轉(zhuǎn)為單層的map了,但是要注意這種場景下結(jié)構(gòu)體和嵌套結(jié)構(gòu)體的字段就需要避免重復(fù)。

        瀏覽 54
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美女操逼 | 大色狼肏匕网 | 国产精品久久久久久久久久清纯 | 337p粉嫩大胆色噜噜噜噜在线播放 | 啊~好大~好爽 | 俺去俺来也在线www色官网 | 国产一区无码 | 操屄视频大全 | 爽爽爽网站 | 男女日皮免费视频 |