Given the following code:
package main
import (
"encoding/json"
"log"
)
type Somefin string
func (s *Somefin) UnmarshalJSON(b []byte) error {
log.Println("Unmarshaling",string(b))
*s = Somefin("~"+string(b)+"~")
return nil
}
type Wat struct {
A, B string
*Somefin
}
func main() {
b := []byte(`{"A":"foo","B":"bar","Somefin":"baz"}`)
w := &Wat{Somefin: new(Somefin)}
err := json.Unmarshal(b,w)
log.Println(w, err)
}
I get the following output:
# go run wat.go
2013/12/14 13:59:17 Unmarshaling {"A":"foo","B":"bar","Somefin":"baz"}
2013/12/14 13:59:17 &{ <nil>} <nil>
So the Somefin
key is for some reason trying to Unmarshal the entire structure instead of just the key it ought to. Am I doing this wrong or is this a bug in the json encoder? This is on go 1.2, btw.
I figured this out.
If you have the struct definition like this:
Then the error I described in the OP happens. But if you do:
Then it doesn't. Check out Chris's comment to this answer for a good explanation of why.
Why you are getting no result at the end
This is no bug in the decoder, it is a bug in your code. You're just assigning another address to the local pointer
s
inUnmarshalJSON
. Corrected code:Semantics of
s = &sn
: Assign the address&sn
tos
. This is similar tos = 42
.Semantics of
*s = sn
: Copy whatever issn
to the place wheres
points to.One requirement for this to work is that
s
points to a valid memory location and must not benil
. Example usage of your code (play):Crucial is the initialization of
Wat
with a newSomefin
so that the pointers
inUnmarshalJSON
is valid (points to the object created withnew(Somefin)
).Why you are getting the whole string in
UnmarshalJSON
Embedding is not polymorphism. While the method set of the embedded object (in your case
Somefin
) is promoted to the outside, this does not mean that the method is now working on the embedding struct rather than the embedded one.Small example (play):
With polymorphism you would expect
Outer.A()
to return2
as methodA()
would operate in the scope ofOuter
and notInner
anymore. With embedding the scope is never changed andA()
will always remain operating onInner
.The same effect prevents your
UnmarshalJSON
from seeing the two membersA
andB
as these are simply not in the scope ofSomefin
:UnmarshalJSON
onWat
becauseUnmarshalJSON
fromSomefin
gets promoted to the outsideSomefin
and delivers the whole input