在Golang中将结构转换为map的函数

时间:2014-05-11 06:14:00

标签: go

我想在Golang中将结构转换为map。如果我可以将JSON标记用作创建的映射中的键(否则默认为字段名称),这也很好。

编辑TL; DR版本,2015年6月15日

如果您想要将结构转换为地图的快速解决方案,请参阅accepted answer,对其进行投票并使用该包。

快乐的编码! :)


原帖

到目前为止我有这个功能,我正在使用反射包但我不太清楚如何使用该包,请耐心等待。

func ConvertToMap(model interface{}) bson.M {
    ret := bson.M{}

    modelReflect := reflect.ValueOf(model)

    if modelReflect.Kind() == reflect.Ptr {
        modelReflect = modelReflect.Elem()
    }

    modelRefType := modelReflect.Type()
    fieldsCount := modelReflect.NumField()

    var fieldData interface{}

    for i := 0; i < fieldsCount; i++ {
        field := modelReflect.Field(i)

        switch field.Kind() {
        case reflect.Struct:
            fallthrough
        case reflect.Ptr:
            fieldData = ConvertToMap(field.Interface())
        default:
            fieldData = field.Interface()
        }

        ret[modelRefType.Field(i).Name] = fieldData
    }

    return ret
}

我还查看了JSON包的源代码,因为它应该包含我需要的实现(或部分内容),但不太了解。

6 个答案:

答案 0 :(得分:103)

我也需要这样的东西。我正在使用一个将结构转换为地图的内部包。我决定用其他基于struct的高级函数开源。看看:

https://github.com/fatih/structs

它支持:

  • 将struct转换为地图
  • 将结构的字段提取为[]string
  • 将结构的值提取为[]values
  • 检查结构是否已初始化
  • 检查传递的接口是结构还是指向struct
  • 的指针

您可以在此处看到一些示例:http://godoc.org/github.com/fatih/structs#pkg-examples 例如,将结构转换为地图很简单:

type Server struct {
    Name    string
    ID      int32
    Enabled bool
}

s := &Server{
    Name:    "gopher",
    ID:      123456,
    Enabled: true,
}

// => {"Name":"gopher", "ID":123456, "Enabled":true}
m := structs.Map(s)

structs包支持匿名(嵌入)字段和嵌套结构。该包提供通过字段标签过滤某些字段。

答案 1 :(得分:18)

structmap[string]interface{}

package main

import (
    "fmt"
    "encoding/json"
)

type MyData struct {
    One   int
    Two   string
    Three int
}

func main() {   
    in := &MyData{One: 1, Two: "second"}

    var inInterface map[string]interface{}
    inrec, _ := json.Marshal(in)
    json.Unmarshal(inrec, &inInterface)

    // iterate through inrecs
    for field, val := range inInterface {
            fmt.Println("KV Pair: ", field, val)
    }
}

go playground here

答案 2 :(得分:8)

这是我过去编写的一个函数,用于将结构转换为地图,使用标记作为键

// ToMap converts a struct to a map using the struct's tags.
//
// ToMap uses tags on struct fields to decide which fields to add to the
// returned map.
func ToMap(in interface{}, tag string) (map[string]interface{}, error){
    out := make(map[string]interface{})

    v := reflect.ValueOf(in)
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }

    // we only accept structs
    if v.Kind() != reflect.Struct {
        return nil, fmt.Errorf("ToMap only accepts structs; got %T", v)
    }

    typ := v.Type()
    for i := 0; i < v.NumField(); i++ {
        // gets us a StructField
        fi := typ.Field(i)
        if tagv := fi.Tag.Get(tag); tagv != "" {
            // set key of map to value in struct field
            out[tagv] = v.Field(i).Interface()
        }
    }
    return out, nil
}

Runnable example here.

注意,如果您有多个具有相同标记值的字段,那么您显然无法将它们全部存储在地图中。如果发生这种情况,可能会谨慎地返回错误。

答案 3 :(得分:4)

我喜欢接受的答案的可导入包,但是它不翻译我的json别名。我的大多数项目都有我导入的辅助函数/类。

这是一个可以解决我的特定问题的函数。


// Converts a struct to a map while maintaining the json alias as keys
func StructToMap(obj interface{}) (newMap map[string]interface{}, err error) {
    data, err := json.Marshal(obj) // Convert to a json string

    if err != nil {
        return
    }

    err = json.Unmarshal(data, &newMap) // Convert to a map
    return
}

总的来说,这就是它的称呼...

package main

import (
    "fmt"
    "encoding/json"
    "github.com/fatih/structs"
)

type MyStructObject struct {
    Email string `json:"email_address"`
}

func main() {
    obj := &MyStructObject{Email: "test@test.com"}

    // My solution
    fmt.Println(StructToMap(obj)) // prints {"email_address": "test@test.com"}

    // The currently accepted solution
    fmt.Println(structs.Map(obj)) // prints {"Email": "test@test.com"}
}

答案 4 :(得分:0)

package main

import (
    "fmt"
    "reflect"
)

type bill struct {
    N1 int
    N2 string
    n3 string
}

func main() {
    a := bill{4, "dhfthf", "fdgdf"}

    v := reflect.ValueOf(a)

    values := make(map[string]interface{}, v.NumField())

    for i := 0; i < v.NumField(); i++ {
        if v.Field(i).CanInterface() {
            values[v.Type().Field(i).Name] = v.Field(i).Interface()
        } else {
            fmt.Printf("sorry you have a unexported field (lower case) value you are trying to sneak past. I will not allow it: %v\n", v.Type().Field(i).Name)
        }
    }

    fmt.Println(values)

    passObject(&values)
}

func passObject(v1 *map[string]interface{}) {
    fmt.Println("yoyo")
}

答案 5 :(得分:0)

map := Structpb.AsMap()

// map is the map[string]interface{}