对接口类型解组 [英] Unmarshal to a interface type

查看:163
本文介绍了对接口类型解组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些代码被我甩了,实际上难倒了 - 我曾经使用RPC和JSON方面的东西,但是我似乎无法让它在RPC上工作,当它在本地正常工作时。

  package main 

import(
log
net
net / rpc
net / rpc / jsonrpc
反映


类型Foo界面{
SayHello( )错误
}

类型fakeFoo结构{
internalValue字符串
}

func NewFakeFoo()* fakeFoo {
f: =& fakeFoo {}
f.internalValue =123456789012347
return f
}

func(m * fakeFoo)SayHello()error {
返回nil
}

类型FooManager结构{
availableFoos [] Foo
}

func NewFooManager()* FooManager {
p:= new(FooManager)
p.availableFoos = make([] Foo,0)
return p
}

AddFoo(mm * FooManager, m Foo){
mm.availableFoo s = 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)是,reflect.TypeOf(* out))

* out = append(in,mm.availableFoos ...)

log.Println ,reflect.TypeOf(* out))

return nil
}

func startServer(mm * FooManager){
server:= rpc。 NewServer()
server.Register(mm)
$ b,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:= NewFak eFoo()

fooHolder:= NewFooManager()
AddFoo(fooHolder,fake1)
go startServer(fooHolder)

log.Println标准函数调用)
var foos [] Foo
fooHolder.GetAvailableFoos(foos,& foos)
log.Println(foos)

log.Println 使用RPC调用)
conn,err:= net.Dial(tcp,localhost:8222)
if err!= nil {
log.Fatalln(err)
}
推迟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)
}

http://play.golang.org/p/HmK-K09D2J



输出结果非常令人惊讶,因为它表示编组出现问题而不是实际数据 - 在wireshark中运行它我可以看到正确格式的数据(I在另一个问题中使用类似的技术成功了),但不能为我的生活得到这个停止抛出编组错误。



运行它的输出如下:

  2015/09/07 10:04:35新增类型* main.fakeFoo 
2015/09/07 10:04:35使用标准函数调用
2015/09/07 10:04:35 availableFoos:[] main.Foo
2015/09/07 10:04:35 * out是[] main .Foo
2015/09/07 10:04:35 out:[] main.Foo
2015/09/07 10:04:35 [0x1870a540]
2015/09/07 10:04:35使用RPC调用
2015/09/07 10:04:35传入!
2015/09/07 10:04:35 [0x1870a540]
2015/09/07 10:04:35 GetAvailableFoos错误:json:无法将对象转换为Go类型main.Foo $ b $的值b退出状态1

我是否缺少界面/类型技巧或者这是Go编组中的错误?

解决方案

所有编组/解组都有这个问题。因为对象本地存在,所以反射器知道底层类型。



您不能解组为接口类型,因为反射器不知道哪些具体类型给新实例接收编组数据。

在一些编组/解组框架中,我们需要附加信息来帮助反射器。例如,在Java Json( jackson )中,我们使用 JsonTypeInfo 注解来指定类的类型,参考这个



对于golang,你可以实现 Unmarshaler 自己的界面。请参阅如何解组JSON?

  // RawString是一个原始编码的JSON对象。 
//它实现了Marshaler和Unmarshaler,可以使用
//延迟JSON解码或预先计算JSON编码。
类型RawString字符串

// MarshalJSON返回* m作为m的JSON编码。
func(m * RawString)MarshalJSON()([] byte,error){
return [] byte(* m),nil
}

// UnmarshalJSON将* m设置为数据副本。
func(m * RawString)UnmarshalJSON(data [] byte)错误{
if m == nil {
return errors.New(RawString:UnmarshalJSON on nil pointer)
}
* m + = RawString(data)
return nil
}

const data =`{i:3,S:{电话:{sales:2223334444}}}`

type A struct {
I int64
S RawString`sql:type:json`

$ b $ func main(){
a:= A {}
err:= json.Unmarshal([] byte(data),& a)
如果错误!= nil {
log.Fatal(Unmarshal failed,err)
}
fmt.Println(完成,a)
}


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?

解决方案

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

这篇关于对接口类型解组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆