英文:
Pass interface slice in function and unmarshal into its items
问题
我可以将结构体的切片转换为[]interface{}
,在函数结束后填充并使用它吗?
以下是问题的完整示例:https://play.golang.org/p/iPijsawEEg
简要描述:
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] 填充了一个 map,而不是对象
}
func main() {
users := make([]User, 5)
data := make([]interface{}, 5)
for i := range users {
data[i] = users[i]
}
loadDBRows(data)
}
这个问题可以很容易地解决单个interface{}
,你可以在完整示例中测试。为什么我不能解决切片的问题?
我想在不使用反射的情况下解决它!有没有一种"真正的方法"来编写通用的 JSON 解析器,以选择的数据结构不使用反射和map[string]interface{}?不想要复杂的代码和额外的操作。
谢谢你的帮助!
英文:
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!
答案1
得分: 1
由于p
是一个接口切片,所以在这一行json.Unmarshal(response.Value, &p[0])
中,你传递的是一个指向interface{}
而不是User
的指针。由于json.Unmarshal
允许将接口作为目标进行解组数据,它不会在interface{}
下寻找另一种类型,而是将json
解码为map
。
你可以将interface{}
已经是指向具体类型的指针,例如data[i] = &users[i]
,然后只需将interface{}
无&
传递给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]) // 注意缺少&
fmt.Println(p)
}
users := make([]User, 5)
data := make([]interface{}, 5)
for i := range users {
data[i] = &users[i] // 注意添加&
}
链接:https://play.golang.org/p/GEbIq9febY
英文:
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 &
}
答案2
得分: 0
一种选择是使用reflect包来访问切片元素。
该函数假设p
是一个切片:
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). // 获取参数的reflect.Value
Index(0). // 获取第一个元素,假设p是一个切片
Addr(). // 取元素的地址
Interface() // 将元素的指针转换为interface{}
json.Unmarshal(response.Value, v)
}
像这样使用loadDBRows
函数。不需要像问题中那样创建[]interface{}
:
func main() {
users := make([]User, 5)
loadDBRows(users)
fmt.Println(users)
}
英文:
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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论