golang通過反射使用json字串呼叫struct的指定方法及返回json結果

changjixiong發表於2016-11-02

起因

  在很多場合會存在這樣一個需求或者想法:提交一個類似這樣的json

{
    "func_name":"FooBarAdd",
    "params":[
        123.4,
        432.1
    ]
}

然後得到一個這樣的json

{
    "func_name":"FooBarAdd",
    "data":[
        555.5
    ]
}

  要達到這樣的目的,必須要先解析提交的json, 獲取到方法名和引數,然後找到擁有這個方法的實體,將引數做適當的型別轉換後呼叫方法,最後將得到的結果包裝成json返回。
  至於這個json是通過http提交與返回,還是通過websocket提交與返回,抑或是通過tcp直接提交與返回,都是可以的。下面來看一下如何達到這個目的。

通過反射獲取struct指定的方法

  假設現在有一個struct是這樣的

type FooBar struct {
}

func (f *FooBar) FooBarAdd(argOne, argTwo float64) float64 {

    return argOne + argTwo
}

對於BarFuncAdd方法,可以這樣呼叫:

foobar := &FooBar{}
resultCallByName :=reflect.ValueOf(foobar).MethodByName("FooBarAdd").
    Call([]reflect.Value{reflect.ValueOf(123.4),reflect.ValueOf(432.1)})

fmt.Println(resultCallByName[0].Float())

也可以這樣呼叫:

foobar := &FooBar{}
resultCallByIndex :=reflect.ValueOf(foobar).Method(0).
    Call([]reflect.Value{reflect.ValueOf(123.4),reflect.ValueOf(432.1)})

fmt.Println(resultCallByIndex[0].Float())

結果都是555.5

通過json字串呼叫

  如果想用一串這樣的json

{
    "func_name":"FooBarAdd",
    "params":[
        123.4,
        432.1
    ]
}

得到一個這樣的結果

{
    "func_name":"FooBarAdd",
    "data":[
        555.5
    ]
}

則可以這樣呼叫

foobar := &FooBar{}

jsonData := `
                {
                    "func_name":"FooBarAdd",
                    "params":[
                        123.4,
                        432.1
                    ]
                }
`
type RequestInfo struct {
    FuncName string        `json:"func_name"`
    Params   []interface{} `json:"params"`
}

type ResultInfo struct {
    FuncName string        `json:"func_name"`
    Data     []interface{} `json:"data"`
}

requestInfo := &RequestInfo{}

json.Unmarshal([]byte(jsonData), &requestInfo)

resultCallByJson :=reflect.ValueOf(foobar).MethodByName(requestInfo.FuncName).
    Call([]reflect.Value{reflect.ValueOf(requestInfo.Params[0]),
    reflect.ValueOf(requestInfo.Params[1])})

result := &ResultInfo{FuncName:requestInfo.FuncName,
    Data: []interface{{resultCallByJson[0].Interface()}}

jsonResult, _ := json.Marshal(&result)
fmt.Printf("%s\n", jsonResult)

適當的完善

  是不是覺得還少了點什麼,比如要是引數個數不匹配怎麼辦?型別轉化怎麼處理?如何做到更通用比如像這樣呼叫:

jsonDataBarFuncSwap := `
{
    "func_name":"BarSwap",
    "params":[
        0.1,
        0.9
    ]
}
`

result:=string(reflectinvoke.InvokeByJson([]byte(jsonStr))

完整的程式碼範例,請看github上面的程式碼

相關文章