跳转至

结构体转 map[string]interface{} 的若干方法

本文介绍了Go语言中将结构体转成 map[string]interface{} 时你需要了解的“坑”,也有你需要知道的若干方法。

我们在Go语言中通常使用结构体来保存我们的数据,例如要存储用户信息,我们可能会定义如下结构体:

Go
1
2
3
4
5
6
7
// UserInfo 用户信息
type UserInfo struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

u1 := UserInfo{Name: "q1mi", Age: 18}

假设现在要将上面的 u1 转换成 map[string]interface{} ,该如何操作呢?

JSON序列化方式

Go
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)
    }
}

看起来没什么问题,但其实这里是有一个“坑”的。那就是Go语言中的 json 包在序列化空接口存放的数字类型(整型、浮点型等)都会序列化成 float64 类型。很显然,出现了一个意料之外的行为,我们需要想办法规避掉。

反射

这里使用反射遍历结构体字段的方式生成 map,具体代码如下:

Go
// ToMap 结构体转为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 {  // 非结构体返回错误提示
        return nil, fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v)
    }

    t := v.Type()
    // 遍历结构体字段
    // 指定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
}

评论