Функция для преобразования структуры в карту в Голанге
Я хочу преобразовать структуру на карту в Голанге. Также было бы хорошо, если бы я мог использовать теги JSON в качестве ключей на созданной карте (в противном случае по умолчанию используется имя поля).
Редактировать TL;DR версия, 15 июня 2015
Если вам нужно быстрое решение для преобразования структуры в карту, посмотрите принятый ответ, добавьте его и используйте этот пакет.
Удачного кодирования!:)
Оригинальный пост
Пока у меня есть эта функция, я использую пакет отражения, но я не очень хорошо понимаю, как использовать пакет, пожалуйста, потерпите меня.
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, потому что он должен содержать мою необходимую реализацию (или ее части), но не слишком понимать.
7 ответов
Мне тоже нужно было что-то подобное. Я использовал внутренний пакет, который преобразовывал структуру в карту. Я решил открыть его с другими struct
основанные функции высокого уровня. Посмотри:
https://github.com/fatih/structs
Поддерживает:
- Конвертировать структуру в карту
- Извлечь поля структуры в
[]string
- Извлечь значения структуры в
[]values
- Проверьте, инициализирована структура или нет
- Проверьте, является ли переданный интерфейс структурой или указателем на структуру
Вы можете увидеть некоторые примеры здесь: http://godoc.org/github.com/fatih/structs Например, преобразование структуры в карту является простым:
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
Пакет поддерживает анонимные (встроенные) поля и вложенные структуры. Пакет обеспечивает фильтрацию определенных полей через теги полей.
От struct
в 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)
}
}
Вот функция, которую я написал в прошлом для преобразования структуры в карту, используя теги в качестве ключей
// 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
}
Работоспособный пример здесь.
Обратите внимание, что если у вас есть несколько полей с одним и тем же значением тега, то вы, очевидно, не сможете сохранить их все на карте. Может быть разумно вернуть ошибку, если это произойдет.
Мне нравится импортируемый пакет для принятого ответа, но он не переводит мои псевдонимы 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(x) // 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"}
}
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")
}
Я немного опоздал, но мне нужна была такая функция, поэтому я написал это. Может разрешать вложенные структуры. По умолчанию использует имена полей, но также может использовать настраиваемые теги. Побочным эффектом является то, что если вы установите для tagTitle const значение 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
}
map := Structpb.AsMap()
// map is the map[string]interface{}