为了账号安全,请及时绑定邮箱和手机立即绑定

包装gob解码器

包装gob解码器

Go
收到一只叮咚 2022-10-24 10:00:36
我将加密的 gobs 存储在 kv 数据库中,并希望在带有密钥和接口的包中具有便利功能,它解密存储的值,将其解码为传递的接口,并且消费者代码可以在没有了解有关正在存储的数据的任何信息。当它与 json 编码器一起工作时,我无法弄清楚为什么我无法使用 gob 写入指针值。简化后的代码如下所示:type Foo struct {    A string    B string}func marshal(i interface{}) ([]byte, error) {    var indexBuffer bytes.Buffer    encoder := gob.NewEncoder(&indexBuffer)    err := encoder.Encode(&i)    return indexBuffer.Bytes(), err}func unmarshal(data []byte, e interface{}) error {    buf := bytes.NewBuffer(data)    dec := gob.NewDecoder(buf)    err := dec.Decode(&e)    fmt.Println("Unmarshal", e)    return err}func marshalJ(i interface{}) ([]byte, error) {    return json.Marshal(i)}func unmarshalJ(data []byte, e interface{}) error {    return json.Unmarshal(data, e)}func main() {    foo := Foo{"Hello", "world!"}    gob.Register(Foo{})    data, err := marshal(foo)    fmt.Println("got", len(data), err)    var bar Foo    err = unmarshal(data, &bar)    fmt.Println("Main err", err)    fmt.Println("Main", bar)    fmt.Println("-------------------------")    data, err = marshalJ(foo)    fmt.Println("got J", len(data), err)    err = unmarshalJ(data, &bar)    fmt.Println("Main J err", err)    fmt.Println("Main J", bar)}(播放链接)unmarshalJ 的行为与我预期的一样,即在 Main 中,我的 bar 变量是 foo 的副本,因为 unmarshalJ 接收到指向 bar 的指针并将其传递。另一方面,unmarshal 函数在本地更改了参数,但并未对 Main 中的栏进行更改。gob.Decode 在内部传递了 reflect.ValueOf,我一直试图避免使用反射,所以我不太明白它的作用。我的两个问题是:是否可以像我想要的那样拥有一个包装器,并像使用 json 编码器一样更改调用者的值?更多的奖励问题,因为这让我觉得我没有完全掌握 go 中的指针:如何更改 unmarshal.e 的值,但当 unmarshal.e 是指向 Main.bar 的指针时不会更改 Main.bar?在这里让我感到困惑的另一件事是,在 unmarshal 内部,我必须将 &e 传递给 Decode ,否则它会以want struct type main.Foo; got non-struct.
查看完整描述

1 回答

?
繁花不似锦

TA贡献1851条经验 获得超4个赞

有两个问题,第一个是你给unmarshal一个指针,然后在unmarshal自己内部创建另一个指针,所以你最终会传递**Foo给 gob 解码器。

第二个问题是您interface{}在函数内获取 'es 的指针。这会以某种方式影响数据的编码方式。如果您将指针传递给函数并且不修改函数内部的变量,那么一切正常。

固定代码如下所示,playground 链接

type Foo struct {

    A string

    B string

}


func marshal(i interface{}) ([]byte, error) {

    var indexBuffer bytes.Buffer

    encoder := gob.NewEncoder(&indexBuffer)

    err := encoder.Encode(i)

    return indexBuffer.Bytes(), err

}


func unmarshal(data []byte, e interface{}) error {

    buf := bytes.NewBuffer(data)

    dec := gob.NewDecoder(buf)

    err := dec.Decode(e)

    fmt.Println("Unmarshal", e)

    return err

}


func marshalJ(i interface{}) ([]byte, error) {

    return json.Marshal(i)

}


func unmarshalJ(data []byte, e interface{}) error {

    return json.Unmarshal(data, e)

}


func main() {

    foo := Foo{"Hello", "world!"}

    gob.Register(Foo{})

    data, err := marshal(&foo)

    fmt.Println("got", len(data), err)

    var bar Foo

    err = unmarshal(data, &bar)

    fmt.Println("Main err", err)

    fmt.Println("Main", bar)

    fmt.Println("-------------------------")

    data, err = marshalJ(foo)

    fmt.Println("got J", len(data), err)

    err = unmarshalJ(data, &bar)

    fmt.Println("Main J err", err)

    fmt.Println("Main J", bar)

}

编辑:作为对评论的回应。


预防这样的问题有时很困难,我认为问题的根源是使用interface{}它会丢弃类型信息,不幸的是,我们对此无能为力(除了为每种类型制作显式解码器功能)。第二个“问题”是 gob 只是忽略了类型不匹配而没有错误的事实,因此没有给我们任何关于我们做错了什么的迹象。


我们在解码端可以做的是添加更严格的类型检查。我们可以要求解码器将解码后的值放入 a 中interface{},然后检查解码后的类型是否与 的类型匹配e:



type Foo struct {

    A string

    B string

}


func marshal(i interface{}) ([]byte, error) {

    var indexBuffer bytes.Buffer

    encoder := gob.NewEncoder(&indexBuffer)

    err := encoder.Encode(&i)

    return indexBuffer.Bytes(), err

}


func unmarshal(data []byte, e interface{}) (err error) {

    buf := bytes.NewBuffer(data)

    dec := gob.NewDecoder(buf)


    eVal := reflect.ValueOf(e)

    eType := eVal.Type()

    if eVal.Kind() != reflect.Ptr {

        return errors.New("e must be a pointer")

    }


    var u interface{}

    err = dec.Decode(&u)

    uVal := reflect.ValueOf(u)

    uType := uVal.Type()

    if eType.Elem() != uType {

        return fmt.Errorf("decoded type '%s' and underlying type of e '%s' not the same", uType.String(), eType.Elem())

    }


    eVal.Elem().Set(uVal)


    return err

}


func main() {

    foo := Foo{"Hello", "world!"}

    gob.Register(Foo{})

    data, err := marshal(foo)

    fmt.Println("got", len(data), err)

    var bar Foo

    var invalid interface{} = bar

    err = unmarshal(data, &invalid)

    fmt.Println("Main err", err)

    fmt.Println("Main", bar)

    err = unmarshal(data, &bar)

    fmt.Println("Main err", err)

    fmt.Println("Main", bar)

}

输出:


got 61 <nil>

Main err decoded type 'main.Foo' and underlying type of e 'interface {}' not the same

Main { }

Main err <nil>

Main {Hello world!}


查看完整回答
反对 回复 2022-10-24
  • 1 回答
  • 0 关注
  • 74 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信