Функция для преобразования структуры в карту в Голанге

Я хочу преобразовать структуру на карту в Голанге. Также было бы хорошо, если бы я мог использовать теги 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{}
Другие вопросы по тегам