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 有赏,i卡绘世画不出
  • ¥15 如何用stata画出文献中常见的安慰剂检验图
  • ¥15 c语言链表结构体数据插入
  • ¥40 使用MATLAB解答线性代数问题
  • ¥15 COCOS的问题COCOS的问题
  • ¥15 FPGA-SRIO初始化失败
  • ¥15 MapReduce实现倒排索引失败
  • ¥15 ZABBIX6.0L连接数据库报错,如何解决?(操作系统-centos)
  • ¥15 找一位技术过硬的游戏pj程序员
  • ¥15 matlab生成电测深三层曲线模型代码