如何通过Go反射设置slice接口值,填充Mongo查询返回的泛型指针数组
Go语言Mongo泛型结果填充问题修复方案
错误根因
Go类型系统中,[]interface{}和[]Person是完全独立的两种类型,即便Person可以存入interface{},切片整体也不支持直接赋值。你直接将[]interface{}类型的反射值写入[]Person类型的接收变量,就会触发类型不匹配的panic。
修复方案
方案1:反射实现(兼容Go 1.18之前版本)
修改后的getListWithCount函数会先获取接收切片的元素类型,逐个转换Mongo返回的文档后再写入切片:
import "errors" import "reflect" import "go.mongodb.org/mongo-driver/bson" import "go.mongodb.org/mongo-driver/bson/primitive" func getListWithCount(ctx context.Context, receiver interface{}) (int, error) { // 原有Mongo查询逻辑保持不变 var mongoResp struct { Total int `bson:"total"` Documents interface{} `bson:"documents"` } if err := cursor.Decode(&mongoResp); err != nil { return 0, err } // 校验入参类型合法性 receiverValue := reflect.ValueOf(receiver) if receiverValue.Kind() != reflect.Ptr { return 0, errors.New("receiver must be a slice pointer") } sliceValue := receiverValue.Elem() if sliceValue.Kind() != reflect.Slice { return 0, errors.New("receiver must point to a slice") } elemType := sliceValue.Type().Elem() docs := mongoResp.Documents.(primitive.A) // 构造和返回结果长度一致的目标类型切片 newSlice := reflect.MakeSlice(sliceValue.Type(), len(docs), len(docs)) for i, rawDoc := range docs { // BSON序列化反序列化完成类型转换 bsonData, err := bson.Marshal(rawDoc) if err != nil { return 0, err } elemPtr := reflect.New(elemType).Interface() if err := bson.Unmarshal(bsonData, elemPtr); err != nil { return 0, err } newSlice.Index(i).Set(reflect.ValueOf(elemPtr).Elem()) } // 赋值给入参接收变量 sliceValue.Set(newSlice) return mongoResp.Total, nil }
方案2:泛型实现(Go 1.18+ 推荐,无反射开销)
使用泛型可以彻底避免反射操作,代码更简洁易维护:
import "log" import "fmt" import "context" import "go.mongodb.org/mongo-driver/bson" func getListWithCount[T any](ctx context.Context) (int, []T, error) { // 原有Mongo查询逻辑保持不变 var mongoResp struct { Total int `bson:"total"` Documents []T `bson:"documents"` } if err := cursor.Decode(&mongoResp); err != nil { return 0, nil, err } return mongoResp.Total, mongoResp.Documents, nil } // 调用示例 type Person struct { Name string `bson:"name"` } func main() { count, persons, err := getListWithCount[Person](context.Background()) if err != nil { log.Fatal(err) } fmt.Println(count) fmt.Println(persons) }
内容的提问来源于stack exchange,提问作者Ado Ren
相关产品推荐
相关产品推荐

