function for converting a struct to map in Golang

huangapple go评论89阅读模式
英文:

function for converting a struct to map in Golang

问题

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

编辑于2020年12月14日

由于structs仓库已归档,您可以使用mapstructure代替。

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

如果您想要将结构体快速转换为map的解决方案,请参阅接受的答案,给它点赞并使用该包。

祝您编码愉快! function for converting a struct to map in Golang


原始帖子

到目前为止,我有这个函数,我正在使用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包的源代码,因为它应该包含我所需的实现(或其中的部分),但我不太理解。

英文:

I want to convert a struct to map in Golang. It would also be nice if I could use the JSON tags as keys in the created map (otherwise defaulting to field name).

Edit Dec 14, 2020

Since structs repo was archived, you can use mapstructure instead.

Edit TL;DR version, Jun 15, 2015

If you want the fast solution for converting a structure to map, see the accepted answer, upvote it and use that package.

Happy coding! function for converting a struct to map in Golang


Original Post

So far I have this function, I am using the reflect package but I don't understand well how to use the package, please bear with me.

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 &lt; 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
}

Also I looked at JSON package source code, because it should contain my needed implementation (or parts of it) but don't understand too much.

答案1

得分: 168

我也有类似的需求。我之前使用一个内部包将结构体转换为映射。我决定将其开源,并与其他基于结构体的高级函数一起使用。你可以看一下这个链接:

https://github.com/fatih/structs

它支持以下功能:

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

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

type Server struct {
    Name    string
    ID      int32
    Enabled bool
}

s := &amp;Server{
    Name:    &quot;gopher&quot;,
    ID:      123456,
    Enabled: true,
}

// =&gt; {&quot;Name&quot;:&quot;gopher&quot;, &quot;ID&quot;:123456, &quot;Enabled&quot;:true}
m := structs.Map(s)

structs包支持匿名(嵌入)字段和嵌套结构体。该包还提供了通过字段标签来过滤特定字段的功能。

英文:

I also had need for something like this. I was using an internal package which was converting a struct to a map. I decided to open source it with other struct based high level functions. Have a look:

https://github.com/fatih/structs

It has support for:

  • Convert struct to a map
  • Extract the fields of a struct to a []string
  • Extract the values of a struct to a []values
  • Check if a struct is initialized or not
  • Check if a passed interface is a struct or a pointer to struct

You can see some examples here: http://godoc.org/github.com/fatih/structs#pkg-examples
For example converting a struct to a map is a simple:

type Server struct {
    Name    string
    ID      int32
    Enabled bool
}

s := &amp;Server{
    Name:    &quot;gopher&quot;,
    ID:      123456,
    Enabled: true,
}

// =&gt; {&quot;Name&quot;:&quot;gopher&quot;, &quot;ID&quot;:123456, &quot;Enabled&quot;:true}
m := structs.Map(s)

The structs package has support for anonymous (embedded) fields and nested structs. The package provides to filter certain fields via field tags.

答案2

得分: 87

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)

	// 遍历inInterface
	for field, val := range inInterface {
    		fmt.Println("KV Pair: ", field, val)
	}
}

在这里查看go playground

英文:

From struct to map[string]interface{}

package main

import (
	&quot;fmt&quot;
	&quot;encoding/json&quot;
)

type MyData struct {
    One   int
    Two   string
    Three int
}

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

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

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

go playground here

答案3

得分: 24

这是我过去编写的一个函数,用于将结构体转换为映射,使用标签作为键:

// ToMap使用结构体的标签将结构体转换为映射。
//
// ToMap使用结构体字段上的标签来决定要添加到返回的映射中的字段。
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()
	}

	// 我们只接受结构体
	if v.Kind() != reflect.Struct {
		return nil, fmt.Errorf("ToMap只接受结构体;得到的是 %T", v)
	}

	typ := v.Type()
	for i := 0; i < v.NumField(); i++ {
		// 获取StructField
		fi := typ.Field(i)
		if tagv := fi.Tag.Get(tag); tagv != "" {
			// 将结构体字段的值设置为映射的键
			out[tagv] = v.Field(i).Interface()
		}
	}
	return out, nil
}

可运行的示例在这里。

注意,如果您有多个具有相同标签值的字段,那么显然无法将它们全部存储在一个映射中。如果发生这种情况,最好返回一个错误。

英文:

Here is a function I've written in the past to convert a struct to a map, using tags as keys

// ToMap converts a struct to a map using the struct&#39;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(&quot;ToMap only accepts structs; got %T&quot;, v)
	}

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

Runnable example here.

Note, if you have multiple fields with the same tag value, then you will obviously not be able to store them all within a map. It might be prudent to return an error if that happens.

答案4

得分: 13

我喜欢可导入的包来解决这个问题,但它不能翻译我的 JSON 别名。我的大多数项目都有一个我导入的辅助函数/类。

下面是一个解决我特定问题的函数。

// 将结构体转换为保持 JSON 别名的键的映射
func StructToMap(obj interface{}) (newMap map[string]interface{}, err error) {
    data, err := json.Marshal(obj) // 转换为 JSON 字符串

    if err != nil {
        return
    }

    err = json.Unmarshal(data, &newMap) // 转换为映射
    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"}

    // 我的解决方案
    fmt.Println(StructToMap(obj)) // 输出 { "email_address": "test@test.com" }

    // 当前被接受的解决方案
    fmt.Println(structs.Map(obj)) // 输出 { "Email": "test@test.com" }
}

希望这能帮到你!

英文:

I like the importable package for the accepted answer, but it does not translate my json aliases. Most of my projects have a helper function/class that I import.

Here is a function that solves my specific problem.


// 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, &amp;newMap) // Convert to a map
	return
}

And in the main, this is how it would be called...

package main

import (
	&quot;fmt&quot;
	&quot;encoding/json&quot;
	&quot;github.com/fatih/structs&quot;
)

type MyStructObject struct {
    Email string `json:&quot;email_address&quot;`
}

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

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

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

答案5

得分: 4

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("抱歉,您有一个未导出的字段(小写)值,您试图绕过它。我不会允许的:%v\n", v.Type().Field(i).Name)
		}
	}

	fmt.Println(values)

	passObject(&values)
}

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

以上是您提供的代码的翻译。

英文:
package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

type bill struct {
	N1 int
	N2 string
	n3 string
}

func main() {
	a := bill{4, &quot;dhfthf&quot;, &quot;fdgdf&quot;}

	v := reflect.ValueOf(a)

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

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

	fmt.Println(values)

	passObject(&amp;values)
}

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

答案6

得分: 1

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

package main

import (
	"fmt"
	"reflect"
)

func StructToMap(val interface{}) map[string]interface{} {
	//你将用于结构体字段的标签名称
	const tagTitle = "kelvin"

	var data map[string]interface{} = make(map[string]interface{})
	varType := reflect.TypeOf(val)
	if varType.Kind() != reflect.Struct {
		//提供的值不是一个结构体,你可以在这里处理它
		fmt.Println("Not a struct")
		return nil
	}

	value := reflect.ValueOf(val)
	for i := 0; i < varType.NumField(); i++ {
		if !value.Field(i).CanInterface() {
			//跳过未导出的字段
			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
}

英文:

I'm a bit late but I needed this kind of feature so I wrote this. Can resolve nested structs. By default, uses field names but can also use custom tags. A side effect is that if you set the tagTitle const to json, you could use the json tags you already have.

package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

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

	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(&quot;Not a struct&quot;)
		return nil
	}

	value := reflect.ValueOf(val)
	for i := 0; i &lt; 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 &amp;&amp; len(tag) &gt; 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
}

答案7

得分: -5

map := Structpb.AsMap()

// map 是 map[string]interface{}

英文:
map := Structpb.AsMap()

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

huangapple
  • 本文由 发表于 2014年5月11日 14:14:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/23589564.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定