包装 gob 解码器

Wrapping gob decoder

我将加密的 gob 存储在 k-v 数据库中,并希望在一个带有密钥和接口的包中有一个方便的功能,它解密存储的值,将其解码为传递的接口和消费者代码无需了解所存储数据的任何信息即可哼唱。

我无法弄清楚为什么我无法使用 gob 写入指针值,当它与 json 编码器一起工作时。简化代码如下所示:

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

(play link)

unmarshalJ 的行为与我预期的一样,在 Main 中,我的 bar 变量是 foo 的副本,因为 unmarshalJ 收到指向 bar 的指针并将其传递。

另一方面,unmarshal 函数在本地更改了参数,但未对​​ Main 中的栏进行更改。在内部 gob.Decode 传递 reflect.ValueOf,我一直试图避免使用反射,所以我真的不明白它的作用。

我的两个问题是:

  1. 是否可以像我想要的那样拥有一个包装器,并像使用 json 编码器一样更改调用者的值?
  2. 更多的奖励问题,因为这让我觉得我没有完全掌握 go 中的指针:如何更改 unmarshal.e 的值,但 Main.bar 不更改 unmarshal.e 是指向 Main.bar 的指针?另一件让我感到困惑的事情是,在 unmarshal 内部,我必须将 &e 传递给 Decode,否则它会因 want struct type main.Foo; got non-struct.
  3. 而失败

有两个问题,第一个是你给 unmarshal 一个指针,然后在 unmarshal 本身内部创建另一个指针,所以你最终将 **Foo 传递给了 gob 解码器.

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

固定代码如下所示,playground link:

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 只是忽略了类型不匹配而没有错误的事实,因此没有给我们任何关于我们做错了什么的指示。

我们可以在解码端做的是添加更严格的类型检查。我们可以要求解码器只将解码后的值放在 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!}