drsh30452 2015-09-06 22:11
浏览 34
已采纳

解组接口类型

I have some code I've been dumped with and am actually stumped - I've worked with RPC and the JSON side of things before but I can't seem to get it to work over 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 no tcp available urgh! http://play.golang.org/p/HmK-K09D2J )

The output is pretty surprising as it indicates something going wrong with the marshalling rather than with the actual data - Running it in wireshark I can see the data being sent in the correct form (I had success using a similar technique in another question) but can't for the life of me get this to stop throwing marshalling bugs.

The output from running this 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 a bug in Go's marshalling?

  • 写回答

1条回答 默认 最新

  • dongpo1599 2015-09-07 02:48
    关注

    All marshaling/unmarshaling has this problem.

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

    You cannot unmarshal to an interface type, because the reflector does not know which concrete type to give to a new instance to receive the marshaled data.

    In some marshal/unmarshal frameworks we need additional information to help the reflector. For example, in Java Json(jackson), we use the JsonTypeInfo annotation to specify the class type, refer to this.

    For golang, you can implement the Unmarshaler interface for your own type yourself. Refer to How do I Unmarshal 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)
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 一道python难题2
  • ¥15 一道python难题
  • ¥15 用matlab 设计一个不动点迭代法求解非线性方程组的代码
  • ¥15 牛顿斯科特系数表表示
  • ¥15 arduino 步进电机
  • ¥20 程序进入HardFault_Handler
  • ¥15 oracle集群安装出bug
  • ¥15 关于#python#的问题:自动化测试
  • ¥20 问题请教!vue项目关于Nginx配置nonce安全策略的问题
  • ¥15 教务系统账号被盗号如何追溯设备