Reputation: 2304
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.
Upvotes: 2
Views: 1390
Reputation: 57599
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
in UnmarshalJSON
. Corrected code:
func (s *Somefin) UnmarshalJSON(b []byte) error {
log.Println("Unmarshaling",string(b))
sn := Somefin("~"+string(b)+"~")
*s = sn
return nil
}
Semantics of s = &sn
: Assign the address &sn
to s
. This is similar to s = 42
.
Semantics of *s = sn
: Copy whatever is sn
to the place where s
points to.
One requirement for this to work is that s
points to a valid memory location and must not be nil
.
Example usage of your code (play):
w := &Wat{Somefin: new(Somefin)}
err := json.Unmarshal(b,w)
log.Printf("%#v (%s)\n", w, err)
Crucial is the initialization of Wat
with a new Somefin
so that the pointer s
in
UnmarshalJSON
is valid (points to the object created with new(Somefin)
).
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):
type Inner struct { a int }
func (i *Inner) A() int { return i.a }
type Outer struct {
*Inner
a int
}
i := &Inner{}
o := Outer{Inner: i}
fmt.Println("Inner.A():", i.A())
fmt.Println("Outer.A():", o.A())
o.a = 2
fmt.Println("Outer.A():", o.A())
With polymorphism you would expect Outer.A()
to return 2
as method A()
would operate in
the scope of Outer
and not Inner
anymore. With embedding the scope is never changed and
A()
will always remain operating on Inner
.
The same effect prevents your UnmarshalJSON
from seeing the two members A
and B
as these
are simply not in the scope of Somefin
:
UnmarshalJSON
on Wat
because UnmarshalJSON
from Somefin
gets promoted to the outsideSomefin
and delivers the whole inputUpvotes: 4
Reputation: 2304
I figured this out.
If you have the struct definition like this:
type Wat struct {
A, B string
Somefin
}
Then the error I described in the OP happens. But if you do:
type Wat struct {
A, B string
Somefin Somefin
}
Then it doesn't. Check out Chris's comment to this answer for a good explanation of why.
Upvotes: 2