将map转换为struct

tgr*_*ger 73 go

我正在尝试在Go中创建一个通用方法,它将填充struct来自a 的使用数据map[string]interface{}.例如,方法签名和用法可能如下所示:

func FillStruct(data map[string]interface{}, result interface{}) {
    ...
}

type MyStruct struct {
    Name string
    Age  int64
}

myData := make(map[string]interface{})
myData["Name"] = "Tony"
myData["Age"]  = 23

result := &MyStruct{}
FillStruct(myData, result)

// result now has Name set to "Tony" and Age set to 23
Run Code Online (Sandbox Code Playgroud)

我知道这可以使用JSON作为中介来完成; 还有另一种更有效的方法吗?

dav*_*ave 81

最简单的方法是使用https://github.com/mitchellh/mapstructure

import "github.com/mitchellh/mapstructure"

mapstructure.Decode(myData, &result)
Run Code Online (Sandbox Code Playgroud)

如果你想自己做,你可以做这样的事情:

http://play.golang.org/p/tN8mxT_V9h

func SetField(obj interface{}, name string, value interface{}) error {
    structValue := reflect.ValueOf(obj).Elem()
    structFieldValue := structValue.FieldByName(name)

    if !structFieldValue.IsValid() {
        return fmt.Errorf("No such field: %s in obj", name)
    }

    if !structFieldValue.CanSet() {
        return fmt.Errorf("Cannot set %s field value", name)
    }

    structFieldType := structFieldValue.Type()
    val := reflect.ValueOf(value)
    if structFieldType != val.Type() {
        return errors.New("Provided value type didn't match obj field type")
    }

    structFieldValue.Set(val)
    return nil
}

type MyStruct struct {
    Name string
    Age  int64
}

func (s *MyStruct) FillStruct(m map[string]interface{}) error {
    for k, v := range m {
        err := SetField(s, k, v)
        if err != nil {
            return err
        }
    }
    return nil
}

func main() {
    myData := make(map[string]interface{})
    myData["Name"] = "Tony"
    myData["Age"] = int64(23)

    result := &MyStruct{}
    err := result.FillStruct(myData)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(result)
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你。我正在使用稍微修改过的版本。http://play.golang.org/p/_JuMm6HMnU (2认同)

yun*_*ace 63

Hashicorp的https://github.com/mitchellh/mapstructure库开箱即用:

import "github.com/mitchellh/mapstructure"

mapstructure.Decode(myData, &result)
Run Code Online (Sandbox Code Playgroud)

第二个result参数必须是结构的地址.


Sim*_*ead 12

你可以做到......它可能会有点丑陋,你将面临一些试验和错误的映射类型..但继承人的基本要点:

func FillStruct(data map[string]interface{}, result interface{}) {
    t := reflect.ValueOf(result).Elem()
    for k, v := range data {
        val := t.FieldByName(k)
        val.Set(reflect.ValueOf(v))
    }
}
Run Code Online (Sandbox Code Playgroud)

工作样本:http://play.golang.org/p/PYHz63sbvL


小智 8

您可以通过 JSON 往返它:

package main

import (
   "bytes"
   "encoding/json"
)

func transcode(in, out interface{}) {
   buf := new(bytes.Buffer)
   json.NewEncoder(buf).Encode(in)
   json.NewDecoder(buf).Decode(out)
}
Run Code Online (Sandbox Code Playgroud)

例子:

package main
import "fmt"

type myStruct struct {
   Name string
   Age  int64
}

func main() {
   myData := map[string]interface{}{
      "Name": "Tony",
      "Age": 23,
   }
   var result myStruct
   transcode(myData, &result)
   fmt.Printf("%+v\n", result) // {Name:Tony Age:23}
}
Run Code Online (Sandbox Code Playgroud)


jac*_*tse 6

  • 最简单的方法是使用encoding/json

仅举例来说:

package main
import (
    "fmt"
    "encoding/json"
)

func Test() {

    dict := make(map[string]interface{})
    dict["id"] = 201902181425
    dict["name"] = "jackytse"
    dict["scores"] = 123.456
    dict["address"] = map[string]string{"home":"address1", "company":"address2"}
    dict["labels"] = []string{"aries", "warmhearted", "frank"}

    jsonbody, err := json.Marshal(dict)
    if err != nil {
        // do error check
        fmt.Println(err)
        return
    }

    type AddressDefine struct {
        Home string
        Company string
    }
    type Student struct {
        Id int64
        Name string
        Scores float32
        Address AddressDefine
        Labels []string
    }
    people := Student{}
    if err := json.Unmarshal(jsonbody, &people); err != nil {
        // do error check
        fmt.Println(err)
    }

    fmt.Printf("%#v\n", people)
}

func main() {
   Test()
}
Run Code Online (Sandbox Code Playgroud)

  • @karthikcru 因为这正是 OP 所说的他们想要避免做的事情,请参阅“我知道这可以使用 JSON 作为中介来完成;是否有另一种更有效的方法来做到这一点?” (2认同)

小智 5

有两个步骤:

  1. 将接口转换为 JSON 字节
  2. 将 JSON 字节转换为结构

下面是一个例子:

dbByte, _ := json.Marshal(dbContent)
_ = json.Unmarshal(dbByte, &MyStruct)
Run Code Online (Sandbox Code Playgroud)