我有一个 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的值。 p>
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