将map转换为struct

huangapple go评论79阅读模式
英文:

Convert map to struct

问题

好的,以下是翻译好的内容:

好的,标题有点误导人。我想要的是以下内容:

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("错误!")
    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)。那个对象来自其他地方,我无法控制。

每次将映射中的每个键值对映射到结构体属性已经很无聊了。但是在每次转换后检查是否一切都正常是混乱的。

除了使用反射之外,是否有更简单的方法来做到这一点?

英文:

Ok, the title is a little bit misleading. What I'm after is as follows:

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
}

Don't ask: Why I'm not using CreateFromMap(int, string, int). That object comes from somewhere else, out of my control.

It's already boring to map each key, value pair in the map to struct properties. But checking if everything is ok or not after each conversion is chaotic.

Is there an easier way of doing this other than reflection?

答案1

得分: 6

假设你不想使用反射是因为你不想自己实现它。在这种情况下,你可以考虑使用一个外部的包来完成这个任务。你可以使用这个外部包

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
}

这样,你可以清楚地将“转换”部分与结构的特定规则处理分开。

英文:

Let's say I assume you don't want to use reflection because you don't want to do it yourself. In this case, what about using an external package that does it for you ?

package main

import &quot;fmt&quot;
import &quot;github.com/mitchellh/mapstructure&quot;

type MyStruct struct {
    Id   int
    Name string
    Age  int
}

func main() {
    var m = make(map[string]interface{})
    m[&quot;Id&quot;] = 17
    m[&quot;Name&quot;] = &quot;foo&quot;
    m[&quot;Age&quot;] = 42
    fmt.Printf(&quot;%+v\n&quot;, m)
    
    res, err := CreateFromMap(m)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf(&quot;%+v\n&quot;, res)
}

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

Output:

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

It has the advantage to work whatever your structure looks like, but it uses reflection internally. Actually, I don't see any "nice" way to do what you want without using reflection and/or repetitive code for each attribute of your structure. The downside, however, is that you will have to use capitalized attributes so that they would be exported to external packages.

Edit (to answer your question in the comments):

On my opinion, if you want to specify additional rules when "creating" the structure, it should be after the decoding operation.
For instance:

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

This way, you will clearly separate the "conversion" part from your structure's specific rules processing.

答案2

得分: 4

你可以直接使用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
}

这段代码的作用是将一个包含键值对的map转换为MyStruct类型的实例。首先,使用json.Marshal函数将map转换为JSON格式的字节流,然后使用json.Unmarshal函数将字节流解析为MyStruct类型的实例。最后,返回解析后的实例和可能出现的错误。

英文:

You can just Marshal/Unmarshal, but property names should match

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

huangapple
  • 本文由 发表于 2015年11月27日 05:11:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/33947044.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定