Golang中将结构转换为Map的函数

mec1mxoz  于 2023-02-06  发布在  Go
关注(0)|答案(7)|浏览(161)

我想在Golang中将一个结构体转换为Map。如果我能在创建的Map中使用JSON标记作为键(否则默认为字段名),那就太好了。

编辑2020年12月14日

由于structs repo已存档,因此可以改用mapstructure

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

如果你想要一个将结构转换为Map的快速解决方案,请查看被接受的答案,投赞成票并使用该软件包。
快乐编码!:)

原始帖子

到目前为止我有这个功能,我使用的是reflect包,但是我不太明白如何使用这个包,请大家多多包涵。

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包的源代码,因为它应该包含我需要的实现(或部分),但不太理解。

83qze16e

83qze16e1#

我也需要这样的东西。我使用了一个内部包,它将一个结构转换为一个Map。我决定将它与其他基于struct的高级函数一起开源。看看:
https://github.com/fatih/structs
它支持:

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

您可以在这里看到一些示例:http://godoc.org/github.com/fatih/structs#pkg-examples例如,将一个结构体转换为Map很简单:

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包支持匿名(嵌入式)字段和嵌套结构。包提供了通过字段标签过滤某些字段。

x3naxklr

x3naxklr2#

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

hc8w905p

hc8w905p3#

下面是我以前编写的一个函数,它使用标记作为键,将结构体转换为Map

// 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
}

可运行example here.
注意,如果你有多个字段有相同的标签值,那么你显然不能把它们都存储在一个Map中,如果发生这种情况,返回一个错误可能是谨慎的。

bbmckpt7

bbmckpt74#

我喜欢接受答案的可导入包,但它不翻译我的json别名。我的大多数项目都有一个我导入的helper函数/类。
这里有一个函数可以解决我的特定问题。

// 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"}
}
czfnxgou

czfnxgou5#

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")
}
kmbjn2e3

kmbjn2e36#

我有点晚了,但我需要这种特性,所以我写了这个。可以解析嵌套的结构。默认情况下,使用字段名,但也可以使用自定义标记。一个副作用是,如果你设置tagTitle常量为json,你可以使用你已经拥有的json标记。

package main

import (
    "fmt"
    "reflect"
)

func StructToMap(val interface{}) map[string]interface{} {
    //The name of the tag you will use for fields of struct
    const tagTitle = "kelvin"

    var data map[string]interface{} = make(map[string]interface{})
    varType := reflect.TypeOf(val)
    if varType.Kind() != reflect.Struct {
        // Provided value is not an interface, do what you will with that here
        fmt.Println("Not a struct")
        return nil
    }

    value := reflect.ValueOf(val)
    for i := 0; i < varType.NumField(); i++ {
        if !value.Field(i).CanInterface() {
            //Skip unexported fields
            continue
        }
        tag, ok := varType.Field(i).Tag.Lookup(tagTitle)
        var fieldName string
        if ok && len(tag) > 0 {
            fieldName = tag
        } else {
            fieldName = varType.Field(i).Name
        }
        if varType.Field(i).Type.Kind() != reflect.Struct {
            data[fieldName] = value.Field(i).Interface()
        } else {
            data[fieldName] = StructToMap(value.Field(i).Interface())
        }

    }

    return data
}
8e2ybdfx

8e2ybdfx7#

map := Structpb.AsMap()

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

相关问题