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

eAb*_*Abi 49 go

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

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

如果您想要将结构转换为映射的快速解决方案,请参阅接受的答案,对其进行upvote并使用该包.

快乐的编码!:)


原帖

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

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
}
Run Code Online (Sandbox Code Playgroud)

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

Fat*_*lan 113

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

https://github.com/fatih/structs

它支持:

  • 将struct转换为地图
  • 将结构的字段提取到 []string
  • 将结构的值提取到a []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)
Run Code Online (Sandbox Code Playgroud)

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

  • 请注意,此项目现已存档且无需维护. (9认同)
  • 哇@Fatih Arslan,这是巨大的.这实际上为`map [string] interface {}提供了"编组"结构,就像`encoding/json.Marhsal()`一样,使用struct标签和all.惊人! (6认同)
  • 这是一种使用“json”标签名称而不是“structs”标签名称的方法。https://github.com/fatih/structs/issues/25 (2认同)
  • 我不想在我的项目中添加另一个依赖项 (2认同)

Zan*_*tsu 24

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

去这里的游乐场

  • 编组然后解组似乎是一种非常低效的数据转换方式。 (3认同)
  • 为了使其更简单,不要使用`var inInterface interface {}`,而是将其更改为`var inInterface map [string] interface {}`.请参阅这里的游乐场:https://play.golang.org/p/woUiMzL_1X (2认同)
  • 这看起来像是骇客,我找不到任何干净或简单的东西 (2认同)
  • 这种方法有问题。在生成的映射中,int 值被转换为 float64,这是意外行为。inInterface["One"] 值的类型为 float64 而不是 int。 (2认同)
  • @d4nyll 你还有其他解决方案吗?当然,不使用反射(直接)。 (2认同)

Edw*_*rdr 10

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

// 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
}
Run Code Online (Sandbox Code Playgroud)

可运行在这里的例子.

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


小智 7

我喜欢接受答案的可导入包,但它不会翻译我的 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
}

Run Code Online (Sandbox Code Playgroud)

总的来说,这就是它的名字......

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"}
}
Run Code Online (Sandbox Code Playgroud)

  • 它可以工作,但它不是最佳解决方案,执行双重编码,在性能至关重要时不应该使用。比创建映射并手动添加键/值慢大约 10 倍。 (2认同)