【问题标题】:How do I convince UnmarshalJSON to work with a slice subtype?如何说服 UnmarshalJSON 使用切片子类型?
【发布时间】:2017-10-22 20:50:27
【问题描述】:

我想要使用 base64 RawURLEncoding 而不是 StdEncoding 在 JSON 中编组和解组的字节切片。通过encoding/json package 没有明显的方法可以做到这一点,这是明智的,所以我想我会创建一个子类型来做到这一点。

type Thing []byte

编组支持很容易:

func (thing Thing) MarshalJSON() ([]byte, error) {
    if thing == nil {
        return []byte("null"), nil
    }
    return []byte(`"` + base64.RawURLEncoding.EncodeToString(thing) + `"`), nil
}

但 Unmarshal 并没有那么多。我追踪了encoding/json source,并想出了:

func (thing Thing) UnmarshalJSON(data []byte) error {
    v := reflect.ValueOf(&thing)
    if len(data) == 0 || data[0] == 'n' { // null
        v.SetBytes([]byte{})
        return nil
    }
    data = data[1 : len(data)-1]
    dst := make([]byte, base64.RawURLEncoding.DecodedLen(len(data)))
    n, err := base64.RawURLEncoding.Decode(dst, data)
    if err != nil {
        return err
    }
    v.SetBytes(Thing(dst[:n]))
    return nil
}

但是在调用SetBytes()时会产生恐慌:

panic: reflect: reflect.Value.SetBytes using unaddressable value [recovered]
    panic: reflect: reflect.Value.SetBytes using unaddressable value

我尝试使用指向切片的指针,但它可以工作(并且不需要反射),但会在我想要使用切片而不是指针的代码的其他地方引起其他挑战。

我猜有两个问题:

  1. 这是使用 RawURLEncoding 将字节切片编组的最佳方式吗?
  2. 如果是这样,我如何说服我的字节切片子类型引用从 RawURLEncoding 格式解码的数据?

【问题讨论】:

    标签: json go reflection marshalling slice


    【解决方案1】:

    使用此代码解组值:

    func (thing *Thing) UnmarshalJSON(data []byte) error {
      if len(data) == 0 || data[0] == 'n' { // copied from the Q, can be improved
        *thing = nil
        return nil
      }
      data = data[1 : len(data)-1]
      dst := make([]byte, base64.RawURLEncoding.DecodedLen(len(data)))
      n, err := base64.RawURLEncoding.Decode(dst, data)
      if err != nil {
        return err
      }
      *thing = dst[:n]
      return nil
    }
    

    重点:

    • 使用指针接收器。
    • 无需反射即可将 []byte 分配给事物。

    playground example

    【讨论】:

    • 好吧,我会被诅咒的。我没有想到只为UnmarshalJSON 使用指针*,但这确实有效!我所做的唯一更改是将 null 案例设置为 nil 而不是空字节片。效果很好,谢谢!
    • Hrm,现在正试图让它作为 JSON 对象键工作,但它不喜欢我的 UnmarshalText。就像UnmarshalJSON(引号除外),但是当我尝试解组为map[*Thing]stringUnmarshaljson: cannot unmarshal object into Go value of type map[*Thing]stringPlayground example.
    • @theory A *Thing 在这种情况下不是有用的映射键(每个解码值都会得到一个新的 *Thing)。将Thing 声明为string 类型以将其用作映射键。见play.golang.org/p/P94GfU2jr3
    猜你喜欢
    • 2011-12-03
    • 2017-06-12
    • 1970-01-01
    • 1970-01-01
    • 2022-01-20
    • 2014-08-08
    • 1970-01-01
    • 2015-07-28
    • 2019-04-13
    相关资源
    最近更新 更多