Why does json.Unmarshal work with reference but not pointer?

JsonGo

Json 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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMattView Question on Stackoverflow
Solution 1 - JsonIntermernetView Answer on Stackoverflow
Solution 2 - JsonEve FreemanView Answer on Stackoverflow
Solution 3 - JsonRuss EganView Answer on Stackoverflow
Solution 4 - JsonFahri Nurul HidayatView Answer on Stackoverflow