While working on Gorp with Indexes i had to solve the problem of how to write into a slice inside a struct passed as an interface{}. A new struct with the filled slice should be returned. Both the struct and the slice are not known at compile time, i only have the fieldname of the slice passed to me at runtime. After some serious headscratching and reading through the reflection code i luckily found a way to do it.
The basic method is:
- Convert the incoming interface i to a reflect.Type t
- Dereference until we have a t which is a Struct
- Create a new reflect.Value v from the Type t
- From v we now can ask for the FieldName of the slice, getting back another reflect.Value s
- As we now have the slice s as a reflect.Value, we need to get the slice elements type
- From the slice elements type we create a new instance of it (reflect.New)
- Write to fields in this newItem using FieldByName (Hardcoded in the example)
- Append the newItem to the slice (the set append was the hard part to find out for me)
- Return the reflect.Value v as an interface
- ???
- Profit - Heureka!!
Output:
Input Type main.Post:
Slice Type []*main.Comment:
Slice Elem Type main.Comment:
Comment 0, Body XYZ 0, PostId 0
Comment 1, Body XYZ 1, PostId 2
Comment 2, Body XYZ 2, PostId 4
Comment 3, Body XYZ 3, PostId 6
Comment 4, Body XYZ 4, PostId 8
Erfolg: Prozess beendet mit Rückgabewert 0.
// This is a demo to show how to convert from a normal struct
// to a reflection type and back to a struct without knowing
// the original one. Input is passed as an Interface and the
// output will be an interface, too.
// Bonus points for writing into an embedded slice
// (= the embedded Comment struct slice in Post)
package main
import (
"errors"
"fmt"
"os"
"reflect"
)
type Post struct {
Id uint64
Title string
Comments []*Comment
}
// holds a single comment bound to a post
type Comment struct {
Id uint64
PostId uint64
Body string
}
func CreateAndFillSlice(i interface{}, sliceName string) (interface{}, error) {
// Convert the interface i to a reflect.Type t
t := reflect.TypeOf(i)
// Check if the input is a pointer and dereference it if yes
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// Check if the input is a struct
if t.Kind() != reflect.Struct {
return nil, errors.New("Input param is not a struct")
}
fmt.Printf("Input Type %v:\n", t)
// Create a new Value from the input type
// this will be returned to the caller
v := reflect.New(t).Elem()
// Get the field named "sliceName" from the input struct, which should be a slice
s := v.FieldByName(sliceName)
if s.Kind() == reflect.Slice {
st := s.Type()
fmt.Printf("Slice Type %s:\n", st)
// Get the type of a single slice element
sliceType := st.Elem()
// Pointer?
if sliceType.Kind() == reflect.Ptr {
// Then dereference it
sliceType = sliceType.Elem()
}
fmt.Printf("Slice Elem Type %v:\n", sliceType)
for i := 0; i < 5; i++ {
// Create a new slice element
newitem := reflect.New(sliceType)
// Set some field in it
newitem.Elem().FieldByName("Body").SetString(fmt.Sprintf("XYZ %d", i))
newitem.Elem().FieldByName("PostId").SetUint(uint64(i * 2))
// This is the important part here - append and set
// Append the newitem to the slice in "v" which will be the output
s.Set(reflect.Append(s, newitem))
}
} else {
return nil, fmt.Errorf("Field %s is not a slice\n", sliceName)
}
// IMPORTANT
// Cast back to the empty interface type
// So the cast back to Post outside will work
return v.Interface(), nil
}
func main() {
var err error
p := Post{Id: 1, Title: "Title 1"}
result, err := CreateAndFillSlice(p, "Comments")
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
// Cast the returned interface to a Post
post := result.(Post)
for i, c := range post.Comments {
fmt.Printf("Comment %d, Body %s, PostId %d\n", i, c.Body, c.PostId)
}
}
betmatik
ReplyDeletekralbet
betpark
tipobet
slot siteleri
kibris bahis siteleri
poker siteleri
bonus veren siteler
mobil ödeme bahis
J40
canlı sex hattı
ReplyDeleteheets
https://cfimi.com/
salt likit
salt likit
6A7X
diyarbakır
ReplyDeletedüzce
edirne
elazığ
erzincan
2DK6
https://saglamproxy.com
ReplyDeletemetin2 proxy
proxy satın al
knight online proxy
mobil proxy satın al
DJC
شركة تنظيف بالدمام 30kKH4B03X
ReplyDeleteشركة تنظيف شقق بالقطيف 7PfGH3WhpX
ReplyDeleteشركة تنظيف خزانات بالهفوف xOKxRZYa6j
ReplyDeleteشركة تركيب جبس بورد بالرياض
ReplyDeleteQcPkVstwG8
شركة تنظيف فلل بالقطيف
ReplyDeleteBf9kRTd9bx