Why does json.Unmarshal work with reference but not pointer?
JsonGoJson Problem Overview
This example from the json.Unmarshal docs (slightly modified for simplicity to use Animal
instead of []Animal
) works, no errors:
Playground link of working example
// ...
var animals Animal
err := json.Unmarshal(jsonBlob, &animals)
// ...
But this slightly modified example doesn't:
Playground link of non-working example
// ...
var animals *Animal
err := json.Unmarshal(jsonBlob, animals)
// ...
It displays this obscure error that really isn't helpful (looks more like a function call than an error IMO):
> json: Unmarshal(nil *main.Animal)
This appears to be because animals
is an uninitialized pointer. But the docs say (emphasis mine):
> Unmarshal unmarshals the JSON into the value pointed at by the pointer. If the pointer is nil, Unmarshal allocates a new value for it to point to.
So why does unmarshaling fail in the second example and show that obscure error?
(Also, is it "unmarshalling" or "unmarshaling" (one L)? The docs use both.)
Json Solutions
Solution 1 - Json
You've encountered an InvalidUnmarshalError (see lines 109 and 110 in decode.go).
>// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal.
// (The argument to Unmarshal must be a non-nil pointer.)
It seems the docs could do with some clarification as the quote above and the comment below from the Unmarshal
source seem to contradict each other.
>If the pointer is nil, Unmarshal allocates a new value for it to point to.
Solution 2 - Json
Because your pointer is nil.
If you initialize it it works: http://play.golang.org/p/zprmV0O1fG
var animals *Animal = &Animal{}
Also, it can be spelled either way (consistency in a single doc would be nice, though): http://en.wikipedia.org/wiki/Marshalling_(computer_science)
Solution 3 - Json
I believe the issue is that, while you can pass a pointer to nil to Unmarshal(), you can't pass a nil pointer value.
A pointer to nil would be like:
var v interface{}
json.Unmarshal(text, &v)
The value of v
is nil, but the pointer to v
is a non-zero pointer address. It's a non-zero pointer, which is pointing to a nil interface{} (which itself is a pointer type). Unmarshal doesn't return an error in this case.
A nil pointer would be like:
var v *interface{}
json.Unmarshal(text, v)
In this case, the type of v is pointer to an interface{}
, but as with any declaration of a var in golang, the initial value of v
is the type's zero-value. So v
is a zero-value pointer, which means it isn't pointing to any valid place in memory.
As mentioned in the https://stackoverflow.com/a/20478917/387176, json.Unmarshal() needs a valid pointer to something, so it can change the something (be it a zero value struct, or a pointer) in place.
Solution 4 - Json
I had a similiar condition before but in a different case. It is related with concept of interface in Go. If a function declares a interface as argument or return value, the caller have to pass or return the reference
In your case, json.Unmarshal
accept interface as second argument