Reputation: 45
When you I use json.Unmarshal
to conctere type i get my values filled as expected.
But if i pass interface{}
instead of struct (for more flexibility), i get map
. How can i fix this... Or is it just antipattern, to pass interfaces.
Example:
package main
import (
"encoding/json"
"fmt"
)
type Response struct {
Success bool `json:"success"`
Data []int64 `json:"data"`
}
func handlerWithInterface(request interface{}, response interface{}) (interface{}, error) {
var err error
// logic with request ....
fixture := []byte("{\"success\": true, \"data\": [20, 21, 22]}")
// assingned to interface
err = json.Unmarshal(fixture, &response)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal responses: %s", err.Error())
}
return response, nil
}
func handler(request interface{}, response Response) (interface{}, error) {
var err error
// logic with request ....
fixture := []byte("{\"success\": true, \"data\": [20, 21, 22]}")
err = json.Unmarshal(fixture, &response)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal responses: %s", err.Error())
}
return response, nil
}
func main() {
req := "some request"
resp := Response{}
result1, err := handlerWithInterface(req, resp)
if err != nil {
panic(err)
}
fmt.Println(result1)
result2, err := handler(req, resp)
if err != nil {
panic(err)
}
fmt.Println(result2)
}
Output:
map[data:[20 21 22] success:true]
{true [20 21 22]}
Upvotes: 1
Views: 1029
Reputation: 120969
The application passes a pointer to an interface{}
containing a Response
value. The json.Unmarshal
function traverses through pointers an interfaces to the last value that's settable, the interface{}
. When the destination is an interface{}
, json.Unmarshal decodes JSON objects to map[string]interface{}
.
Fix by passing a pointer to resp
in main. With this change, the unmarshal function will use resp
is the target for decoding.
resp := Response{}
result1, err := handlerWithInterface(req, &resp) // <-- add & on this line
It's not necessary to take the address of the response
in handlerWithInterface
. Pass the pointer from main
on through to the json.Unmarshal
.
func handlerWithInterface(request interface{}, response interface{}) (interface{}, error) {
var err error
// logic with request ....
fixture := []byte("{\"success\": true, \"data\": [20, 21, 22]}")
err = json.Unmarshal(fixture, response) // <-- remove & on this line
if err != nil {
return nil, fmt.Errorf("failed to unmarshal responses: %s", err.Error())
}
return response, nil
}
Upvotes: 3