根据字符串动态创建对象

问题描述:

I'm trying to dynamically create structs based on a string.

In the below example reflect.TypeOf &c and &c1 are different because I return interface{} from makeInstance. TypeOf c and c1 are the same.

My question is how do I change the way I handle the output of makeInstance so it creates an object identical to c1 but will still allow me to create objects identical to b1 also?

type Car struct {
    Make  int `json:"make"`
    Model int `json:"model"`
}

type Bus struct {
    Seats int `json:"seats"`
    Route int `json:"route"`
}

var typeRegistry = make(map[string]reflect.Type)

func init() {
    typeRegistry["Car"] = reflect.TypeOf(Car{})
    typeRegistry["Bus"] = reflect.TypeOf(Bus{})

}

func makeInstance(name string) interface{} {
    v := reflect.New(typeRegistry[name]).Elem()

    return v.Interface()
}

func main() {

    c := makeInstance("Car")
    b := makeInstance("Bus")

    var b1 Bus
    var c1 Car

    fmt.Println(reflect.TypeOf(&c))
    fmt.Println(reflect.TypeOf(&c1))
    fmt.Println(reflect.TypeOf(c))  
    fmt.Println(reflect.TypeOf(c1))

Edit:

My overall outcome is to have a program that reads a json config file that will list the types of objects I want to go off and hit a rest api and collect e.g.

{
    "auth":[{
                "username": "admin",
                "password": "admin"
            }],
    "transport":[
            {
                "vehicle":["car", "bus"]
            }]
}

When looping through vehicle it would hit an api and perform a query and return data. I would then want to create an array of the which ever vehicle is included in the config and unmarshal the json response into it. I'm trying to create the objects dynamically so I could avoid having to check if vehicle = car, if vehicle = bus etc as I will eventually have many types of vehicles but may not always have every vehicle and it seems long winded.

The function returns values of type Car and Bus as written. If you want the variable in main to have a specific type, then use a type assertion:

c := makeInstance("Car").(Car)

If your goal is to get a pointer to values of these types, then return the pointer from makeInstance:

func makeInstance(name string) interface{} {
    return reflect.New(typeRegistry[name]).Interface()
}

You probably should stop and read about interface values type assertions. Go is not a dynamically typed language and what you are trying to do will fail with high probability:

As long as you are working with interface{} you simply cannot access the fields (Make, Model, Seats, Route, ...) without reflection. If you want to write x.Make you must have a x of type Car or *Car (and not interface{}).

To get from c of type interface{} to e.g. a Car you must type assert:

var car Car = c.(Car)

Note that you cannot do dynamic type assertions (without reflection) and that c.(Car) will fail if c contains e.g. a Bus. So after json.Unmarshaling into a generic interface{} you will have to switch on the known type and assert to that type. Which means you will write dedicated code for each type anyway.