登录
首页 >  Golang >  Go问答

利用反射在切片中设置接口值

来源:stackoverflow

时间:2024-03-22 18:51:33 134浏览 收藏

本文介绍了如何利用反射在切片中设置接口值。作者试图使用反射将从 MongoDB 获取的值设置到指针数组中,但遇到了错误。正确的解决方案是首先将值解码为 bson.RawValue,然后使用 Unmarshal 解码为接收者。本文还提供了自定义 bson.Unmarshaler 和动态创建结构体类型等替代方案,以解决这个问题。

问题内容

我想构建一个函数,它采用通用指针数组并根据 mongo 结果填充该列表。

我不知道如何将从 mongo 获得的值设置到我的指针数组中。在下面的尝试中,程序出现以下错误:reflect.set:类型 []interface {} 的值无法分配给类型 []person

当我打印找到的总数/文档时,它符合我的预期。所以我认为问题在于反思。

func getListWithCount(ctx context.Context, receiver interface{}) (int, error) {
    //my mongo query here
    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)
    docs := []interface{}(mongoResp.Documents.(primitive.A))
    receiverValue.Elem().Set(reflect.ValueOf(docs))

    return mongoResp.Total, nil 
}

type Person struct {
    Name string `bson:"name"`
}

func main() {
    var persons []Person
    count, err := getListWithCount(context.Background(), &persons)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(count)
    fmt.Println(persons)
}

正确答案


您应该能够首先将其解码为 bson.RawValue,然后将 Unmarshal 解码为 receiver

func getlistwithcount(ctx context.context, receiver interface{}) (int, error) {
    //my mongo query here
    var mongoresp struct {
        total     int           `bson:"total"`
        documents bson.rawvalue `bson:"documents"`
    }
    if err := cursor.decode(&mongoresp); err != nil {
        return 0, err
    }
    if err := mongoresp.documents.unmarshal(receiver); err != nil {
        return 0, err
    }
    return mongoresp.total, nil 
}

您还可以将其实现为自定义 bson.Unmarshaler

type mongoresp struct {
    total     int         `bson:"total"`
    documents interface{} `bson:"documents"`
}

func (r *mongoresp) unmarshalbson(data []byte) error {
    var temp struct {
        total     int           `bson:"total"`
        documents bson.rawvalue `bson:"documents"`
    }
    if err := bson.unmarshal(data, &temp); err != nil {
        return err
    }

    r.total = temp.total
    return temp.documents.unmarshal(r.documents)
}

这样您就可以在函数中使用它,如下所示:

func getlistwithcount(ctx context.context, receiver interface{}) (int, error) {
    //my mongo query here
    mongoresp := mongoresp{documents: receiver}
    if err := cursor.decode(&mongoresp); err != nil {
        return 0, err
    }
    return mongoresp.total, nil 
}

动态创建与查询文档匹配的结构体类型。详情请参阅下面的评论。

func getListWithCount(receiver interface{}) (int, error) {
    dst := reflect.ValueOf(receiver).Elem()

    //  Your mongo query here

    // Create a struct type that matches the document.
    doct := reflect.StructOf([]reflect.StructField{
        reflect.StructField{Name: "Total", Type: reflect.TypeOf(0), Tag: `bson:"total"`},
        reflect.StructField{Name: "Documents", Type: dst.Type(), Tag: `bson:"documents"`},
    })

    // Decode to a value of the type.
    docp := reflect.New(doct)
    if err := cursor.Decode(docp.Interface()); err != nil {
        return 0, err
    }
    docv := docp.Elem()

    // Copy the Documents field to *receiver.
    dst.Set(docv.Field(1))

    // Return the total
    return docv.Field(0).Interface().(int), nil
}

文中关于的知识介绍,希望对你的学习有所帮助!若是受益匪浅,那就动动鼠标收藏这篇《利用反射在切片中设置接口值》文章吧,也可关注golang学习网公众号了解相关技术文章。

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>