Description
What version of Go are you using (go version
)?
go version go1.10.3 linux/amd64
What operating system and processor architecture are you using (go env
)?
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/deuill/.cache/go-build"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/deuill/.go"
GORACE=""
GOROOT="/usr/lib/go"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build634949289=/tmp/go-build -gno-record-gcc-switches"
What did you do?
Link to Play: https://play.golang.org/p/37E1QHWofMy
Passing a pointer to a struct
(anonymous or not) as type interface{}
to json.Unmarshal
will have the original type replaced with a map[string]interface{}
(correctly decoded, however). It appears the difference between correct and incorrect result is changing:
var nopointer interface{} = Object{}
to:
var nopointer = Object{}
In the example above. A interface{}
containing a pointer to a struct
works as expected, e.g.:
var pointer interface{} = &Object{}
is the same as:
var pointer = &Object{}
What did you expect to see?
The following results, in order of desirability:
- The correct
struct
type (Object
, for the example above) populated with data from the JSON object given. - An error denoting the inability to unmarshal into the underlying type given.
- The behaviour above documented in the documentation for
json.Unmarshal
.
What did you see instead?
None of the above? The use case that led to this issue for me was giving constructors types, where these types would be used as "templates" of sorts for populating with data in subsequent requests. For example:
type Decoder interface {
Decode(*http.Request) (interface{}, error)
}
type RequestHandler func(interface{}) (interface{}, error)
type Request struct {
Value interface{}
}
func (r Request) Decode(r *http.request) (interface{}, error) {
// Read body from request
// ...
if err := json.Unmarshal(body, &r.Value); err != nil {
return nil, err
}
return r, nil
}
type User struct {
Name string `json:"name"`
}
func NewHandler(decoder Decoder, handler RequestHandler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
req, err := decoder.Decode(r)
if err != nil {
return
}
resp, err := handler(req)
if err != nil {
return
}
// Encode response into ResponseWriter etc.
// ...
}
}
It is implied, then, that NewHandler
is called with the bare type:
NewHandler(Request{Value: User{}}, userHandler)
Which will give "fresh" values to the RequestHandler
each time, as they emerge from Request.Decode
. If given a pointer, i.e.:
NewHandler(Request{Value: &User{}}, userHandler)
Previous requests will populate the pointer to User
, leaving garbage data for future requests.
Apologies for the perhaps obtuse example.