将地图转换为结构

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?

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 "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
", m)

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

func CreateFromMap(m map[string]interface{}) (MyStruct, error) {
    var result MyStruct
    err := mapstructure.Decode(m, &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, &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
}

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

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, &result)
    return result, err
}