Golang: Having trouble with nested JSON Unmarshaler

匿名 (未验证) 提交于 2019-12-03 02:31:01

问题:

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.

回答1:

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:

  1. JSON library sees UnmarshalJSON on Wat because UnmarshalJSON from Somefin gets promoted to the outside
  2. JSON library cannot find any matching element in Somefin and delivers the whole input


回答2:

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.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!