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.
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
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)
).
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):
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
:
- JSON library sees
UnmarshalJSON
onWat
becauseUnmarshalJSON
fromSomefin
gets promoted to the outside - JSON library cannot find any matching element in
Somefin
and delivers the whole input
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.
来源:https://stackoverflow.com/questions/20587157/golang-having-trouble-with-nested-json-unmarshaler