Can i pass into function a slice of structs, converted to []interface{}
, fill it and use after function end work?
Here is full example of the problem https://play.golang.org/p/iPijsawEEg
Short describe:
type DBResponse struct {
Rows int `json:"rows"`
Error string `json:"error"`
Value json.RawMessage `json:"value"`
}
type User struct {
Id int `json:"id"`
Name string `json:"name"`
}
func loadDBRows(p []interface{}) {
var response DBResponse
someDataFromDB := []byte("{\"rows\":1, \"error\": \"\", \"value\": {\"name\":\"John\", \"id\":2}}")
json.Unmarshal(someDataFromDB, &response)
json.Unmarshal(response.Value, &p[0])
fmt.Println(p)//p[0] filled with map, not object
}
func main() {
users := make([]User, 5)
data := make([]interface{}, 5)
for i := range users {
data[i] = users[i]
}
loadDBRows(data)
}
This problem can be easily solved for single interface{}
, u can test in at full example. Why i can't solve it for slice?
I want to do it without reflect! Is there any "true way" to write universal json parser to selected data struct without reflect and map[string]interface{}? Don't want complicated code and extra operations
Thank you for help!
Since p
is a slice of interfaces, on this line json.Unmarshal(response.Value, &p[0])
you're passing a pointer to an interface{}
not to a User
and since json.Unmarshal
allows interfaces as the destination to which to unmarshal the data, it doesn't look under the interface{}
for another type and just decodes the json
into a map
.
What you can do is to have the interface{}
already be a pointer to a concrete type e.g. data[i] = &users[i]
then you just pass the interface{}
without &
to json.Unmarshal
.
func loadDBRows(p []interface{}) {
var response DBResponse
someDataFromDB := []byte("{\"rows\":1, \"error\": \"\", \"value\": {\"name\":\"John\", \"id\":2}}")
json.Unmarshal(someDataFromDB, &response)
json.Unmarshal(response.Value, p[0]) // notice the missing &
fmt.Println(p)
}
users := make([]User, 5)
data := make([]interface{}, 5)
for i := range users {
data[i] = &users[i] // notice the added &
}
One option is to use the reflect package to access the slice elements.
The function assumes that p
is a slice:
func loadDBRows(p interface{}) {
var response DBResponse
someDataFromDB := []byte("{\"rows\":1, \"error\": \"\", \"value\": {\"name\":\"John\", \"id\":2}}")
json.Unmarshal(someDataFromDB, &response)
v := reflect.ValueOf(p). // get reflect.Value for argument
Index(0). // get first element assuming that p is a slice
Addr(). // take address of the element
Interface() // get the pointer to element as an interface{}
json.Unmarshal(response.Value, v)
}
Use loadDBRows
like this. There's no need to create the []interface{}
as in the question:
func main() {
users := make([]User, 5)
loadDBRows(users)
fmt.Println(users)
}