如何使用反射动态构建结构切片

时间:2020-04-08 16:57:38

标签: go reflection go-reflect

我试图用指针构造Book结构的切片,但无法在Go中进行反射。

[] * Book结构体指针的Book slice,请注意,scanResults方法可能会接收任何类型的分片,而不仅是Book结构体。因此,我希望在运行时动态地构建切片

您能否让我知道下面的代码片段出了什么问题?

package main

import (
  "reflect"
"errors"
"fmt"
)

type Book struct {
    Id    int
    Title string
    Price float32
}

func main() {
    var dest []*Book
    scanResults(&dest)
}

func scanResults(dest interface{}) error{
   resultsFromExternalSource := []interface{}{10 , "user-name" , float32(22)}

    value := reflect.ValueOf(dest)
    if value.Kind() != reflect.Ptr {
        return errors.New("must pass a pointer, not a value, to scan results into struct destination")
    }

    sliceElement := reflect.TypeOf(dest).Elem()
    if sliceElement.Kind() != reflect.Slice {
        return fmt.Errorf("expected %s but got %s", reflect.Slice, sliceElement.Kind())
    }

    structPtr := sliceElement.Elem()
    if structPtr.Kind() != reflect.Ptr {
        return fmt.Errorf("expected %s but got %s", reflect.Ptr, structPtr.Kind())
    }

    structElemType := reflect.TypeOf(structPtr).Elem()
    if structElemType.Kind() != reflect.Struct {
        return fmt.Errorf("expected %s but got %s", reflect.Struct, structElemType.Kind())
    }

 structRecordInterface := reflect.New(structElemType).Elem().Interface() // create a new struct
            structRecordType := reflect.TypeOf(structRecordInterface)
            structRecordValue := reflect.ValueOf(structRecordType)

    for i, result := range resultsFromExternalSource {



                if structRecordValue.Field(i).CanSet() {
                    structRecordValue.Field(i).Set(reflect.ValueOf(result))
                } else {
                    varName := structRecordType.Field(i).Name
                    varType := structRecordType.Field(i).Type
                    return fmt.Errorf("cannot scan results into passed struct destination as the struct field %v with %v type is not settable", varName, varType)
                }
       }
     return nil

}

https://play.golang.org/p/O9j4RobQqMy

1 个答案:

答案 0 :(得分:3)

您快到了。这是一些带有注释的工作代码:

var errBadArg = errors.New("must pass pointer to slice of pointer to struct")

func scanResults(dest interface{}) error {
    resultsFromExternalSource := [][]interface{}{
        {10, "user-name", float32(22)},
        {20, "i-love-reflect", float32(100)},
    }

    // Get reflect.Value for the destination confirm that
    // the destination is a pointer to a slice of pointers
    // to a struct. The tests can be omitted if it's acceptable
    // to panic on bad input argument.

    destv := reflect.ValueOf(dest)

    if destv.Kind() != reflect.Ptr {
        return errBadArg
    }

    // Deference the pointer to get the slice.
    destv = destv.Elem()
    if destv.Kind() != reflect.Slice {
        return errBadArg
    }

    elemt := destv.Type().Elem()
    if elemt.Kind() != reflect.Ptr {
        return errBadArg
    }

    // "deference" the element type to get the struct type.
    elemt = elemt.Elem() 
    if elemt.Kind() != reflect.Struct {
        return errBadArg
    }


    // For each row in the result set...
    for j, row := range resultsFromExternalSource {

        // Return error if more columns than fields in struct.
        if len(row) > elemt.NumField() {
            return errors.New("result larger than struct")
        }

        // Allocate a new slice element.
        elemp := reflect.New(elemt)

        // Dereference the pointer for field access.
        elemv := elemp.Elem()

        for i, col := range row {
            fieldv := elemv.Field(i)
            colv := reflect.ValueOf(col)

            // Check to see if assignment to field will work
            if !colv.Type().AssignableTo(fieldv.Type()) {
                return fmt.Errorf("cannot assign %s to %s in row %d column %d", colv.Type(), fieldv.Type(), j, i)
            }

            // Set the field.
            fieldv.Set(colv)
        }

        // Append element to the slice.
        destv.Set(reflect.Append(destv, elemp))
    }
    return nil
}

Run it on the playground