Reputation: 1413
Context: I'm trying to take any struct
, and fill it with random data.
My big sticking point at the moment is that if a struct
has a field that is a slice with a pointer type (ie. []*Foo
), I'm unable to figure out how to create data for that struct using reflection.
Here's what my function currently looks like:
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
}
This works great on a slice without pointer types, but here's an example of some structs that it has trouble with:
type Parent struct {
Name string
Age int
Children []*Child
}
type Child struct {
Name string
Age int
}
Where if I created a Parent{}
and passed it into randFill(Parent{})
, it has trouble generating values for *Child
because when it gets to this line:
newC := slice.Index(j)
The value of newC
at this point, when handling the slice of []*Child
is (*Child)(nil)
, and it's a reflect.Value
type.
newC := slice.Index(j)
fmt.Printf("%#v, %T", newC, newC) // Outputs: (*Child)(nil), reflect.Value
There's something I'm missing around being able to initialize a pointer type, from a reflect.Value
, or I've created the incorrect type of slice, and that is the root of my problems?
Upvotes: 0
Views: 1046
Reputation:
A simple approach is to write a function that fills a reflect.Value with random data. The function calls itself recursively for structured values (slices, structs, ...).
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())
}
There are a couple of simplifications in this code compared to the code in the question. The first is that this answer avoids the reflect.ValueOf
and .Interface()
calls by working with reflect.Value
. The second is that pointers are handled as a top-level case, thus eliminating the need for pointer related code in the slice element and field code.
Upvotes: 1
Reputation: 51577
When you find put that the element is a pointer, you have to create an instance of the type the pointer points to, but you're creating a new pointer. Try this:
if newC.Kind() == reflect.Ptr {
ncInt := reflect.New(newC.Type().Elem())
newC = ncInt.Elem()
}
Upvotes: 1