卓越飞翔博客卓越飞翔博客

卓越飞翔 - 您值得收藏的技术分享站
技术文章64334本站已运行4115

如何通过反射传递 Type 来调用 Go 中的泛型函数

如何通过反射传递 type 来调用 go 中的泛型函数

问题内容

我有一个 Go 应用程序,可以接收同一 API 的不同 JSON 响应,因此我尝试编写一个自定义解组器,它将尝试处理每个可能的 JSON 响应,直到找到正确的响应。

为此,我创建了一个通用函数来解组 JSON,并且我想使用从反射获得的类型来调用它。

例如:

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "reflect"
)

type JSONHandler struct{}

func (handler *JSONHandler) Unmarshal(data []byte) error {
    t := reflect.TypeOf(*handler)

    for i := 0; i < t.NumField(); i++ {
        fieldType := t.Field(i)

        fmt.Printf("Trying to unmarshal: %sn", fieldType.Name)
        result, err := unmarshal[fieldType](data) // fieldType (variable of type reflect.StructField) is not a typecompiler (NotAType)

        if err == nil {
            fmt.Printf("result: %sn", result)
            break
        }
    }

    return nil
}

func unmarshal[T interface{}](data []byte) (*T, error) {
    obj := new(T)

    reader := bytes.NewReader(data)
    decoder := json.NewDecoder(reader)
    decoder.DisallowUnknownFields()

    err := decoder.Decode(obj)
    if err != nil {
        return nil, err
    }
    return obj, nil
}

type User struct {
    Username *string `json:"username,omitempty"`
    Password *string `json:"password,omitempty"`
}

type UserError struct {
    Error *string `json:"error,omitempty"`
    Code  *int    `json:"code,omitempty"`
}

type UserOrError struct {
    User      *User
    UserError *UserError
    JSONHandler
}

var userJson = `{ "username": "[email protected]", "password": "password" }`

func main() {
    user := new(UserOrError)
    result := user.Unmarshal([]byte(userJson)) // { User: something, UserError: nil }
}

但是,Go 编译器给了我这个错误:fieldType (reflect.StructField 类型的变量) 不是类型编译器 (NotAType)

有没有办法将反射参数传递给泛型函数?


正确答案


这个问题超出了 Go 通用功能的范围。

使用 reflect.New 创建一个给定该值的reflect.Type的值。

Go 没有继承。问题中的 Unmarshal 方法对没有字段的类型进行操作。通过将“处理程序”传递给普通函数来修复。

// Unmarshal unmarshals data to each field type in handler
// and returns the first successful result. The handler argument
// must be a pointer to a struct.
func Unmarshal(handler any, data []byte) (any, error) {
    t := reflect.TypeOf(handler).Elem()
    for i := 0; i < t.NumField(); i++ {
        fieldType := t.Field(i)
        v := reflect.New(fieldType.Type)
        decoder := json.NewDecoder(bytes.NewReader(data))
        decoder.DisallowUnknownFields()
        err := decoder.Decode(v.Interface())
        if err == nil {
            return v.Elem().Interface(), err
        }
    }
    return nil, errors.New("no matching type")
}

像这样使用 Marshal 函数:

type UserOrError struct {
    User      *User
    UserError *UserError
}

var userJson = `{ "username": "[email protected]", "password": "password" }`

result, err := Unmarshal(&UserOrError{}, []byte(userJson)) // { User: something, UserError: nil }
fmt.Printf("err=%v, result=%#vn", err, result)

https://www.php.cn/link/c76e4b2fa54f8506719a5c0dc14c2eb9

卓越飞翔博客
上一篇: Yocto 无法交叉编译 GoLang Wails 应用程序
下一篇: 如何将多子域网站与独立的数据库关联起来?
留言与评论(共有 0 条评论)
   
验证码:
隐藏边栏