Unmarshal for interface type

I have the code that I fell into, and actually a dead end - I worked with RPC and the JSON side, but I can not get it to work on RPC when it works fine locally.

package main import ( "log" "net" "net/rpc" "net/rpc/jsonrpc" "reflect" ) type Foo interface { SayHello() error } type fakeFoo struct { internalValue string } func NewFakeFoo() *fakeFoo { f := &fakeFoo{} f.internalValue = "123456789012347" return f } func (m *fakeFoo) SayHello() error { return nil } type FooManager struct { availableFoos []Foo } func NewFooManager() *FooManager { p := new(FooManager) p.availableFoos = make([]Foo, 0) return p } func AddFoo(mm *FooManager, m Foo) { mm.availableFoos = append(mm.availableFoos, m) log.Println("Added type ", reflect.TypeOf(m)) } func (mm *FooManager) GetAvailableFoos(in []Foo, out *[]Foo) error { log.Println("availableFoos:", reflect.TypeOf(mm.availableFoos)) log.Println("*out is", reflect.TypeOf(*out)) *out = append(in, mm.availableFoos...) log.Println("Out is:", reflect.TypeOf(*out)) return nil } func startServer(mm *FooManager) { server := rpc.NewServer() server.Register(mm) l, e := net.Listen("tcp", ":8222") if e != nil { log.Fatal("listen error:", e) } for { conn, err := l.Accept() log.Println("Incoming!") if err != nil { log.Fatal(err) } go server.ServeCodec(jsonrpc.NewServerCodec(conn)) } } func main() { fake1 := NewFakeFoo() fooHolder := NewFooManager() AddFoo(fooHolder, fake1) go startServer(fooHolder) log.Println("Using standard function call") var foos []Foo fooHolder.GetAvailableFoos(foos, &foos) log.Println(foos) log.Println("Using RPC call") conn, err := net.Dial("tcp", "localhost:8222") if err != nil { log.Fatalln(err) } defer conn.Close() c := jsonrpc.NewClient(conn) err = c.Call("FooManager.GetAvailableFoos", foos, &foos) if err != nil { log.Println(foos) log.Fatal("GetAvailableFoos error:", err) } log.Println("Success: ", foos) } 

(also here, but tcp urgh is not available! http://play.golang.org/p/HmK-K09D2J )

The conclusion is rather surprising, as it indicates what is wrong with sorting, not with actual data. Running in wirehark. I see that the data is sent in the correct form (I had success using a similar technique in another matter), but cannot for my life make me stop throwing marching errors.

The output from this run is as follows:

 2015/09/07 10:04:35 Added type *main.fakeFoo 2015/09/07 10:04:35 Using standard function call 2015/09/07 10:04:35 availableFoos: []main.Foo 2015/09/07 10:04:35 *out is []main.Foo 2015/09/07 10:04:35 Out is: []main.Foo 2015/09/07 10:04:35 [0x1870a540] 2015/09/07 10:04:35 Using RPC call 2015/09/07 10:04:35 Incoming! 2015/09/07 10:04:35 [0x1870a540] 2015/09/07 10:04:35 GetAvailableFoos error:json: cannot unmarshal object into Go value of type main.Foo exit status 1 

Am I missing an interface / type trick or is this an error in Go sorting?

0
source share
1 answer

All unmarshaling has this problem.

You can marshal from an interface type variable because the object exists locally, so the reflector knows the base type.

You cannot disable the interface type because the reflector does not know which concrete type it should give to the new instance to receive marshaled data.

In some marshal / non-marshal structures, we need additional information to help the reflector. For example, in Java Json ( jackson ), we use the JsonTypeInfo annotation to indicate the type of class, see this .

For golang, you can implement the Unmarshaler interface for your own type. See How to disable JSON?

 // RawString is a raw encoded JSON object. // It implements Marshaler and Unmarshaler and can // be used to delay JSON decoding or precompute a JSON encoding. type RawString string // MarshalJSON returns *m as the JSON encoding of m. func (m *RawString) MarshalJSON() ([]byte, error) { return []byte(*m), nil } // UnmarshalJSON sets *m to a copy of data. func (m *RawString) UnmarshalJSON(data []byte) error { if m == nil { return errors.New("RawString: UnmarshalJSON on nil pointer") } *m += RawString(data) return nil } const data = `{"i":3, "S":{"phone": {"sales": "2223334444"}}}` type A struct { I int64 S RawString `sql:"type:json"` } func main() { a := A{} err := json.Unmarshal([]byte(data), &a) if err != nil { log.Fatal("Unmarshal failed", err) } fmt.Println("Done", a) } 
+4
source

Source: https://habr.com/ru/post/1241457/


All Articles