将接口切片传递给函数,并将其解组为其元素。

huangapple go评论91阅读模式
英文:

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 &
}

https://play.golang.org/p/GEbIq9febY

答案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)
}

playground示例

英文:

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)
}

playground example

huangapple
  • 本文由 发表于 2017年3月27日 05:45:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/43034937.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定