我如何说服 UnmarshalJSON 使用切片子类型?
How do I convince UnmarshalJSON to work with a slice subtype?
我想要使用 base64 RawURLEncoding
instead of StdEncoding
. There's no obvious way to do this through the encoding/json package 在 JSON 中编组和解组的字节片,这是明智的,所以我想我会创建一个子类型来完成它。
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
我尝试使用一个指向切片的指针,它可以工作(并且不需要反射),但在我的代码中的其他地方导致了其他挑战,这些挑战想要使用切片而不是指针。
所以有两个问题,我猜:
- 这是使用
RawURLEncoding
获取要编组的字节片的最佳方式吗?
- 如果是这样,我如何说服我的字节切片子类型引用从
RawURLEncoding
格式解码的数据?
使用此代码解组值:
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
}
要点:
- 使用指针接收器。
- 不需要反射来将 [] 字节分配给事物。
我想要使用 base64 RawURLEncoding
instead of StdEncoding
. There's no obvious way to do this through the encoding/json package 在 JSON 中编组和解组的字节片,这是明智的,所以我想我会创建一个子类型来完成它。
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
我尝试使用一个指向切片的指针,它可以工作(并且不需要反射),但在我的代码中的其他地方导致了其他挑战,这些挑战想要使用切片而不是指针。
所以有两个问题,我猜:
- 这是使用
RawURLEncoding
获取要编组的字节片的最佳方式吗? - 如果是这样,我如何说服我的字节切片子类型引用从
RawURLEncoding
格式解码的数据?
使用此代码解组值:
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
}
要点:
- 使用指针接收器。
- 不需要反射来将 [] 字节分配给事物。