将地图转换为struct

时间:2015-11-26 21:11:17

标签: go

好的,标题有点误导。我所追求的如下:

type MyStruct struct {
    id   int
    name string
    age  int
}

func CreateFromMap(m map[string]interface{}) (MyStruct, error) {
    var (
        id   int
        name string
        age  int
        ok   bool
    )
    err := errors.New("Error!")
    id, ok = m["id"].(int)
    if !ok {
        return nil, err
    }
    name, ok = m["name"].(string)
    if !ok {
        return nil, err
    }
    age, ok = m["age"].(int)
    if !ok {
        return nil, err
    }
    return MyStruct{id, name, age}, nil
}

不要问:为什么我没有使用CreateFromMap(int, string, int)。那个对象来自其他地方,不受我的控制。

将地图中的每个键值对映射到struct属性已经很无聊了。但是在每次转换后检查是否所有内容都是ok是混乱的。

除了反思之外,还有更简单的方法吗?

2 个答案:

答案 0 :(得分:2)

让我们假设你不想使用反思,因为你不想自己做。在这种情况下,使用external package为你做什么呢?

package main

import "fmt"
import "github.com/mitchellh/mapstructure"

type MyStruct struct {
    Id   int
    Name string
    Age  int
}

func main() {
    var m = make(map[string]interface{})
    m["Id"] = 17
    m["Name"] = "foo"
    m["Age"] = 42
    fmt.Printf("%+v\n", m)

    res, err := CreateFromMap(m)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("%+v\n", res)
}

func CreateFromMap(m map[string]interface{}) (MyStruct, error) {
    var result MyStruct
    err := mapstructure.Decode(m, &result)
    return result, err
}

输出:

map[Age:42 Name:foo Id:17]
{Id:17 Name:foo Age:42}

它的优点是可以处理您的结构,但它在内部使用反射。实际上,我没有看到任何好的"如何在不使用结构的每个属性的反射和/或重复代码的情况下执行您想要的操作。然而,缺点是您必须使用大写属性,以便将它们导出到外部包。

编辑(在评论中回答您的问题):

在我看来,如果你想在"创建"时指定其他规则。结构,应该是解码后的操作。 例如:

func CreateFromMap(m map[string]interface{}) (MyStruct, error) {
    var result MyStruct
    err := mapstructure.Decode(m, &result)
    if err != nil {
         return result, err
    }
    if result.Age <= 0 {
        result.Age = 0
    }
    if result.Name == "" {
        return result, errors.New("empty name is not allowed")
    }

    return result, err
}

通过这种方式,您可以明确区分&#34;转换&#34;部分来自您的结构的特定规则处理。

答案 1 :(得分:2)

你可以只是Marshal / Unmarshal,但属性名称应匹配

func CreateFromMap(m map[string]interface{}) (MyStruct, error) {
    data, _ := json.Marshal(m)
    var result MyStruct
    err := json.Unmarshal(data, &result)
    return result, err
}