如何在Go中将动态创建的结构作为非指针对象传递

时间:2013-03-28 22:55:32

标签: pointers reflection go

我正在尝试将参数动态传递给URL路由处理函数。我认为可以使用反射包将值的映射从URL转换为具有恰好是匿名结构的一个参数的函数。我已经得到了创建结构以传递给处理函数但它最终成为结构的指针。如果我改变处理函数的签名以期望一个指针,那么创建的结构最终成为指针的指针,我想。无论如何,这是代码(恐慌如下):

链接:http://play.golang.org/p/vt_wNY1f08

package main

import (
    "errors"
    "fmt"
    "net/http"
    "reflect"
    "strconv"
    "github.com/gorilla/mux"
)


func mapToStruct(obj interface{}, mapping map[string]string) error {
    dataStruct := reflect.Indirect(reflect.ValueOf(obj))

    if dataStruct.Kind() != reflect.Struct {
        return errors.New("expected a pointer to a struct")
    }

    for key, data := range mapping {
        structField := dataStruct.FieldByName(key)

        if !structField.CanSet() {
            fmt.Println("Can't set")
            continue
        }

        var v interface{}

        switch structField.Type().Kind() {
        case reflect.Slice:
            v = data
        case reflect.String:
            v = string(data)
        case reflect.Bool:
            v = string(data) == "1"
        case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
            x, err := strconv.Atoi(string(data))
            if err != nil {
                return errors.New("arg " + key + " as int: " + err.Error())
            }
            v = x
        case reflect.Int64:
            x, err := strconv.ParseInt(string(data), 10, 64)
            if err != nil {
                return errors.New("arg " + key + " as int: " + err.Error())
            }
            v = x
        case reflect.Float32, reflect.Float64:
            x, err := strconv.ParseFloat(string(data), 64)
            if err != nil {
                return errors.New("arg " + key + " as float64: " + err.Error())
            }
            v = x
        case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
            x, err := strconv.ParseUint(string(data), 10, 64)
            if err != nil {
                return errors.New("arg " + key + " as int: " + err.Error())
            }
            v = x
        default:
            return errors.New("unsupported type in Scan: " + reflect.TypeOf(v).String())
        }

        structField.Set(reflect.ValueOf(v))
    }
    return nil
}

type RouteHandler struct {
    Handler interface{}
}

func (h RouteHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    t := reflect.TypeOf(h.Handler)

    handlerArgs := reflect.New(t.In(0)).Interface()

    if err := mapToStruct(handlerArgs, mux.Vars(req)); err != nil {
        panic(fmt.Sprintf("Error converting params"))
    }

    f := reflect.ValueOf(h.Handler)

    args := []reflect.Value{reflect.ValueOf(handlerArgs)}
    f.Call(args)

    fmt.Fprint(w, "Hello World")
}


type App struct {
    Router mux.Router
}

func (app *App) Run(bind string, port int) {
    bind_to := fmt.Sprintf("%s:%d", bind, port)
    http.Handle("/", &app.Router)
    http.ListenAndServe(bind_to, &app.Router)
}

func (app *App) Route(pat string, h interface{}) {
    app.Router.Handle(pat, RouteHandler{Handler:h})
}


func home(args struct{Category string}) {
    fmt.Println("home", args.Category)  
}


func main() {
    app := &App{}
    app.Route("/products/{Category}", home)
    app.Run("0.0.0.0", 8080)
}

恐慌:

2013/03/28 18:48:43 http: panic serving 127.0.0.1:51204: reflect: Call using *struct { Category string } as type struct { Category string }
/usr/local/Cellar/go/1.0.3/src/pkg/net/http/server.go:589 (0x3fb66)
    _func_004: buf.Write(debug.Stack())
/usr/local/Cellar/go/1.0.3/src/pkg/runtime/proc.c:1443 (0x11cdb)
    panic: reflect·call(d->fn, d->args, d->siz);
/usr/local/Cellar/go/1.0.3/src/pkg/reflect/value.go:428 (0x484ba)
    Value.call: panic("reflect: " + method + " using " + xt.String() + " as type " + targ.String())
/usr/local/Cellar/go/1.0.3/src/pkg/reflect/value.go:334 (0x47c3a)
    Value.Call: return v.call("Call", in)
/Users/matt/Workspaces/Go/src/pants/pants.go:86 (0x2f36)
    RouteHandler.ServeHTTP: f.Call(args)
/Users/matt/Workspaces/Go/src/pants/pants.go:1 (0x347c)
    (*RouteHandler).ServeHTTP: package main
/Users/matt/Workspaces/Go/src/github.com/gorilla/mux/mux.go:86 (0x5a699)
    com/gorilla/mux.(*Router).ServeHTTP: handler.ServeHTTP(w, req)
/usr/local/Cellar/go/1.0.3/src/pkg/net/http/server.go:669 (0x337b6)
    (*conn).serve: handler.ServeHTTP(w, w.req)
/usr/local/Cellar/go/1.0.3/src/pkg/runtime/proc.c:271 (0xfde1)
    goexit: runtime·goexit(void)

1 个答案:

答案 0 :(得分:3)

在您的reflect.Value对象上调用Elem()。

引自The Laws of Reflection文章:

  

为了得到p指向的,我们称之为值的Elem方法,它通过指针指向