function for converting a struct to map in Golang

Go

Go Problem Overview


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! :)


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 < 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.

Go Solutions


Solution 1 - Go

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 := &Server{
    Name:    "gopher",
    ID:      123456,
    Enabled: true,
}

// => {"Name":"gopher", "ID":123456, "Enabled":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.

Solution 2 - Go

From struct to map[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

Solution 3 - Go

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'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.

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.

Solution 4 - Go

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

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

package main

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

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

func main() {
    obj := &MyStructObject{Email: "[email protected]"}

    // My solution
    fmt.Println(StructToMap(obj)) // prints {"email_address": "[email protected]"}

    // The currently accepted solution
    fmt.Println(structs.Map(obj)) // prints {"Email": "[email protected]"}
}

Solution 5 - Go

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

Solution 6 - Go

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 (
	"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
}

Solution 7 - Go

map := Structpb.AsMap()

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioneAbiView Question on Stackoverflow
Solution 1 - GoFatih ArslanView Answer on Stackoverflow
Solution 2 - GoZangetsuView Answer on Stackoverflow
Solution 3 - GoEdwardrView Answer on Stackoverflow
Solution 4 - GoEldyView Answer on Stackoverflow
Solution 5 - GoderekView Answer on Stackoverflow
Solution 6 - GoKelvin View Answer on Stackoverflow
Solution 7 - GoAman AgarwalView Answer on Stackoverflow