go - 解码为接口(interface)类型

标签 go marshalling rpc json-rpc

我有一些代码被丢弃了,实际上我被难住了——我以前使用过 RPC 和 JSON 方面的东西,但是当它在本地工作正常时,我似乎无法让它在 RPC 上工作。

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)
}

(也在这里,但没有可用的 tcp 呃!http://play.golang.org/p/HmK-K09D2J)

输出非常令人惊讶,因为它表明编码出了问题,而不是实际数据 - 在 wireshark 中运行它,我可以看到数据以正确的形式发送(我在另一个问题中使用类似的技术成功) 但我这辈子都不能让它停止抛出编码错误。

运行结果如下:

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

我是否遗漏了接口(interface)/类型技巧,或者这是 Go 编码中的错误?

最佳答案

所有编码/解码都有这个问题。

您可以从接口(interface)类型变量编码,因为该对象存在于本地,所以反射器知道底层类型。

您不能解码为接口(interface)类型,因为反射器不知道将哪个具体类型提供给新实例以接收编码数据。

在一些编码/解码框架中,我们需要额外的信息来帮助反射器。比如Java Json(jackson)中,我们使用JsonTypeInfo注解来指定类类型,引用this .

对于 golang,您可以实现 Unmarshaler你自己的类型自己的界面。引用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)
}

关于go - 解码为接口(interface)类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58078347/

相关文章:

go - Go中 "...Type"是什么意思?

go - 如何创建动态路线

go - 访问Docker Swarm集群上Couchbase容器的IP地址

java - 动态根元素 JAXB?

java - Java 和 C 之间的远程过程调用

python - 通过 C++ 的 RPC 启动新线程会导致进程僵尸化?

mongodb - 使用GridFS使用mongo-go-driver将文件上传到MongoDB

c# - C# 中的联合 - 不正确对齐或与非对象字段重叠

json - 编码类型时如何将方法结果嵌入到 JSON 输出中?

gwt - 在 GWT-RPC 中发送 ArrayList 作为参数