Обход структуры с отражением, создание нового среза типа указателя с отражением - PullRequest
0 голосов
/ 11 января 2020

Контекст: я пытаюсь взять любой struct и заполнить его случайными данными.

Моя большая проблема в данный момент заключается в том, что если struct имеет поле, которое является срезом с типом указателя (ie. []*Foo) я не могу понять, как создать данные для этой структуры с помощью отражения.

Вот как выглядит моя функция в настоящее время:

func randFill(in interface{}) interface{} {
    t := reflect.TypeOf(in)
    v := reflect.ValueOf(in)

    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }

    switch v.Kind() {
    case reflect.Struct:
        newStr := reflect.New(t).Elem()
        for i := 0; i < t.NumField(); i++ {
            newV := randFill(reflect.New(t.Field(i).Type).Interface())
            newStr.Field(i).Set(reflect.ValueOf(newV))
        }

        return newStr.Interface()
    case reflect.Slice:
        num := rand.Intn(10)
        slice := reflect.MakeSlice(v.Type(), num, num)

        for j := 0; j < num; j++ {
            newC := slice.Index(j)
            if newC.Kind() == reflect.Ptr {
                ncInt := reflect.New(newC.Type())
                newC = ncInt.Elem()
            }
            gen := randFill(newC.Interface())
            slice.Index(j).Set(reflect.ValueOf(gen))
        }

        return slice.Interface()
    //
    // ... there are other cases down here for handling primitives
    //
    }

    return nil
}

Это прекрасно работает на слайсе без типов указателей, но вот пример некоторых структур, с которыми у него возникают проблемы:

type Parent struct {
    Name     string
    Age      int
    Children []*Child
}

type Child struct {
    Name string
    Age  int
}

Где, если я создал Parent{} и передал его в randFill(Parent{}), у него возникают проблемы при генерации значений для *Child, потому что, когда он попадает в эту строку:

newC := slice.Index(j)

Значение newC в этой точке при обработке среза []*Child равно (*Child)(nil) и это тип reflect.Value.

newC := slice.Index(j)
fmt.Printf("%#v, %T", newC, newC) // Outputs: (*Child)(nil), reflect.Value

Есть что-то, чего мне не хватает в способности инициализировать тип указателя из reflect.Value, или я создал неверный тип слайса, и это root моих проблем?

Ответы [ 2 ]

1 голос
/ 11 января 2020

Простой подход - написать функцию, которая заполняет отражение. Значение случайными данными. Функция вызывает себя рекурсивно для структурированных значений (срезы, структуры, ...).

func randFillValue(v reflect.Value) {
    switch v.Kind() {
    case reflect.Ptr:
        v.Set(reflect.New(v.Type().Elem()))
        randFillValue(v.Elem())
    case reflect.Struct:
        for i := 0; i < v.NumField(); i++ {
            randFillValue(v.Field(i))
        }
    case reflect.Slice:
        num := rand.Intn(10)
        v.Set(reflect.MakeSlice(v.Type(), num, num))
        for i := 0; i < num; i++ {
            randFillValue(v.Index(i))
        }
    case reflect.Int:
        v.SetInt(10)  // TODO: fill with random int
    case reflect.String:
        v.SetString("random string") // TODO: fill with random string
    }
    // TODO: add other reflect.Kind
}

// randFill fills the value pointed to pv with random values.
func randFill(pv interface{}) {
    randFillValue(reflect.ValueOf(pv).Elem())
}

Запустить его на игровой площадке .

Есть несколько упрощения в этом коде по сравнению с кодом в вопросе. Во-первых, этот ответ позволяет избежать вызовов reflect.ValueOf и .Interface(), работая с reflect.Value. Во-вторых, указатели обрабатываются как регистр верхнего уровня, что устраняет необходимость в связанном с указателем коде в элементе слайса и коде поля.

1 голос
/ 11 января 2020

Когда вы обнаружите, что элемент является указателем, вы должны создать экземпляр типа, на который указывает указатель, но вы создаете новый указатель. Попробуйте это:

if newC.Kind() == reflect.Ptr {
    ncInt := reflect.New(newC.Type().Elem())
    newC = ncInt.Elem()
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...