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.
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.
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
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.
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: "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"}
}
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")
}
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
}
map := Structpb.AsMap()
// map is the map[string]interface{}
Related
In Go, I'm trying to pass an interface{} to the statement.Exec() function from go-sqlite3. I'm sure this is a solved problem, but I cannot figure it out.
Basically I have a struct with the row data which I want to pass to a function that will insert it to a sqlite db. The thing is I want to be able to programmatically control what goes into the statement.Exec() function
Here is an excerpt:
type hostRows struct {
domain string
}
type clientRows struct {
name string
}
func main() {
...
data := hostRows{domain: "dom.com"}
insertRow(sqliteDatabase, data)
data2 := clientRows{name: "bob"}
insertRow(sqliteDatabase, data2)
...
}
func insertRow(db *sql.DB, row interface{}) {
insertSQL := "INSERT INTO table(col) VALUES (?)"
statement, _ := db.Prepare(insertSQL)
statement.Exec(row) // here's the issue, how can I extract the element in the interface to pass it to the function for Exec to understand
}
I know that in this example, I could hard code the row type to the struct and type statement.Exec(row.(hostRows).domain), but now the code will break to when the client struct is passed.
here is the deceleration for the Exec function
func (s *Stmt) Exec(args ...interface{}) (Result, error)
I've tried playing with reflect but it hasn't worked for me so far.
My only solution for the moment is using a switch condition that could check and prepare the right command for Exec, but this is less than dodgy.
type hostRows struct {
domain string
}
type clientRows struct {
name string
}
func main() {
...
data := hostRows{domain: "dom.com"}
insertRow(sqliteDatabase, 1, data)
data2 := clientRows{name: "bob"}
insertRow(sqliteDatabase, 2, data2)
...
}
func insertRow(db *sql.DB, i int, row interface{}) {
insertSQL := "INSERT INTO table(col) VALUES (?)"
statement, _ := db.Prepare(insertSQL)
// This basically could be a working solution, but I'm sure there is a better one
switch i {
case 1:
data := row.(hostRows)
statement.Exec(data.domain)
case 2:
data := row.(clientRows)
statement.Exec(data.name)
}
}
edit: corrected the INSERT statement ; forget the columns. corrected statement.Exec(row.domain) to statement.Exec(row.(hostRows).domain)
edit2: added second example
Remember that in order for reflect.Interface() to work, you must export the fields. To achieve what you want using reflection, you could try something like this:
type hostRows struct {
//Should export field to read it using reflect.Value.Interface()
Domain string
}
type clientRows struct {
//Should export field to read it using reflect.Value.Interface()
Name string
}
func insertRow(db *sql.DB, i int, row interface{}) {
rv := reflect.ValueOf(row)
var args []interface{}
for i := 0; i < rv.NumField(); i++ {
args = append(args, rv.Field(i).Interface())
}
db.Exec("Insert Satement...", args...)
}
Given this struct
type A struct {
b *B
c string
d string
}
I want to check with reflect which fields have a struct behind the pointer type, so specifically I want to create a condition that would only be true when iterating over field b. From what I've tried using the reflect documentation I always end up with an invalid Value kind which doesn't allow me to go further as every subsequent method panics.
package main
import (
"fmt"
"reflect"
)
type A struct {
b *B
c string
d string
}
type B struct {
}
func main() {
val := reflect.ValueOf(A{})
for i := 0; i < val.Type().NumField(); i++ {
if val.Field(i).Kind() == reflect.Ptr {
fmt.Println(reflect.Indirect(val.Field(i)).Kind())
}
fmt.Println(val.Field(i).Kind())
}
}
https://play.golang.org/p/oRry3ZubRxI
You get invalid value, because the val.b pointer field is nil, and you can't dereference a nil pointer. If you want your code to work, you have to initialize it with a valid pointer:
val := reflect.ValueOf(A{b: &B{}})
With this change it works and outputs (try it on the Go Playground):
struct
ptr
string
If you want it to work without having to initialize the pointer, then you have to work on the types and not values:
val := reflect.ValueOf(A{})
t := val.Type()
for i := 0; i < t.NumField(); i++ {
if ft := t.Field(i).Type; ft.Kind() == reflect.Ptr {
fmt.Println(ft.Elem().Kind())
}
fmt.Println(t.Field(i).Type.Kind())
}
This outputs the same, try this one on the Go Playground.
I have a struct and I am trying to iterate through all fields to determine whether or not the field is a map (or a pointer to a map). I'm able to compare field kind to reflect.Map for non-pointer fields, but I am having trouble doing the same for pointer fields. If the field has a value I can use Elem() to determine the pointer field type, but if the field is nil that method won't work and I get "invalid." Any ideas on how to accomplish this? Playground is here.
package main
import (
"fmt"
"reflect"
)
type Item struct {
itemMap map[string]interface{}
itemMapPtr *map[string]interface{}
}
func main() {
item := new(Item)
printTypes(*item)
}
func printTypes(item Item) {
itemVal := reflect.ValueOf(item)
for i := 0; i < itemVal.NumField(); i++ {
fieldVal := itemVal.Field(i)
if fieldVal.Kind() == reflect.Ptr {
fieldVal = fieldVal.Elem() // This is only helpful if the field is not nil
}
fmt.Println(fieldVal.Kind())
}
}
Work with types instead of values:
t := reflect.TypeOf(item)
for i := 0; i < t.NumField(); i++ {
ft := t.Field(i).Type
if ft.Kind() == reflect.Ptr {
ft = ft.Elem()
}
fmt.Println(ft.Kind())
}
playground example
I'm trying to do something like this:
Define structs with tags named env:
type Env struct {
Port string `env:"PORT"`
}
Call some function which will get the environment variable names using os.Getenv and put set it in the struct.
Right now, I have this:
package main
import (
"fmt"
"os"
"reflect"
)
func ParseEnv(t interface{}, v interface{}) {
it := reflect.TypeOf(t)
for i := 0; i < it.NumField(); i++ {
field := it.Field(i)
value := os.Getenv(field.Tag.Get("env"))
if value == "" {
continue
}
reflect.ValueOf(v).Elem().FieldByName(field.Name).SetString(value)
}
}
type Env struct {
Port string `env:"PORT"`
DatabaseURL string `env:"DATABASE_URL"`
}
func main() {
os.Setenv("PORT", "8080")
os.Setenv("DATABASE_URL", "postgres://user:pass#host:5432/my-db")
env := Env{}
ParseEnv(env, &env)
fmt.Println(env)
}
http://play.golang.org/p/b8uPPVo4aV
But, as you can see, I have to pass both the reference and the pointer to my function.
While this works, it is very ugly (at least I think it is).
If I try to pass the pointer only, I can't get the type right (because it will be an *interface{}) and, if I pass only the reference, I can't set the values using reflect (even if I could, it would not work).
Is there a sane way of doing this?
Below is a "saner" way of doing what you want. You will notice that, instead of passing in two copies of the struct, we only need a pointer to the struct.
func ParseEnv(val interface{}) {
ptrRef := reflect.ValueOf(val)
if ptrRef.Kind() != reflect.Ptr {
panic("pointer to struct expected")
}
ref := ptrRef.Elem()
if ref.Kind() != reflect.Struct {
panic("pointer to struct expected")
}
refType := ref.Type()
for i := 0; i < refType.NumField(); i++ {
field := refType.Field(i)
value := os.Getenv(field.Tag.Get("env"))
if value == "" {
continue
}
ref.Field(i).SetString(value)
}
}
The above function should be invoked in the following way:
ParseEnv(&env)
Example: https://play.golang.org/p/_BwWz2oUql
I have a function that is being generated using reflection and reflect.MakeFunc, so I don't actually have the return type until runtime.
Inside the template function that MakeFunc is using, is there a way to determine the return type of the concrete function being templated?
Essentially, is there a way to determine the return type iof the currently executing function at runtime?
I know about the Out method:
fn.Type().Out(0)
And I can find the return type of a function easily enough?
But is there a way to find the return type of the currently executing function (as opposed to an explicit passed function reference).
You should check fn.Type().Out(0).Kind(), for example:
func main() {
fnTmpl := func(in []reflect.Value) []reflect.Value {
return []reflect.Value{in[0]}
}
makeFn := func(fptr interface{}) {
fn := reflect.ValueOf(fptr).Elem()
fn.Set(reflect.MakeFunc(fn.Type(), fnTmpl))
}
var nFn func(int) int
makeFn(&nFn)
kind := reflect.TypeOf(nFn).Out(0).Kind()
switch kind {
case reflect.Int:
fmt.Println("int")
}
}
In the case you are talking about, the return type of the currently executing function is always []reflect.Type (because that is what a function passed to reflect.MakeFunc must return). What you really want is the return type of the reflect.makeFuncStub function that called your function.
There is no way to get that (except perhaps some strange inspection of the call stack), but you can make an enhanced version of MakeFunc that provides the information:
package main
import (
"fmt"
"reflect"
)
// MakeFunc is like reflect.MakeFunc, but fn has an extra argument, retType, which
// is passed the desired return type.
func MakeFunc(typ reflect.Type, fn func(args []reflect.Value, retType reflect.Type) (results []reflect.Value)) reflect.Value {
if n := typ.NumOut(); n != 1 {
panic("wrong number of return values")
}
rt := typ.Out(0)
return reflect.MakeFunc(typ, func(args []reflect.Value) (results []reflect.Value) {
return fn(args, rt)
})
}
func makeReturnOne(fptr interface{}) {
fn := reflect.ValueOf(fptr).Elem()
fn.Set(MakeFunc(fn.Type(), returnOne))
}
func returnOne(args []reflect.Value, retType reflect.Type) []reflect.Value {
ret := reflect.New(retType).Elem()
switch retType.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
ret.SetInt(1)
case reflect.Float32, reflect.Float64:
ret.SetFloat(1.0)
default:
panic("returnOne only supports int and float types")
}
r := ret.Interface()
fmt.Printf("returning %v as %T\n", r, r)
return []reflect.Value{ret}
}
func main() {
var r1f func() float64
var r1i func() int
makeReturnOne(&r1f)
makeReturnOne(&r1i)
fmt.Println(r1f())
fmt.Println(r1i())
}
I might have misinterpreted what you are trying to achieve, but why not just take the kind of the value you are returning? Modifying OneOfOne's example as follows:
fnTmpl := func(in []reflect.Value) (res []reflect.Value) {
res = []reflect.Value{in[0]}
fmt.Println("Returned:", res[0].Kind())
return res
}
Playground: http://play.golang.org/p/EujmxyGRrI