Go 中 JSON 的特殊使用技巧
修改序列化后的 Key 值
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
// output
{"id": 12, "name": "laily"}
数字转字符串
前端 js 对 int64 的处理可能会因为溢出导致无法准确处理,因此我们期望可以返回字符串类型。
type User struct {
ID int64 `json:"id,string"`
}
// output
{"id":"123131"}
忽略零值
type User struct {
ID int `json:"id,omitempty"`
Name string `json:"name,omitempty"`
}
如果 ID 是 0,Name 是空字符串则在序列化的结果里不会有这两个 key。
强制忽略某些 key
type User struct {
ID int `json:"id"`
Name string `json:"-"`
}
无论 Name 是什么值,序列化后都没有这个 key。
定制 Marshaler/Unmarshal
type ID int64
func (i ID) MarshalJSON() ([]byte, error) {
if i < 0 {
return nil, xerrors.Errorf("err: ID(%d) must greater than 0", i)
}
str, err := seed.EncodeInt64([]int64{int64(i)})
if err != nil {
return nil, err
}
return []byte(str), nil
}
func (i *ID) UnmarshalJSON(data []byte) error {
is, err := seed.DecodeInt64WithError(string(data))
if err != nil {
return xerrors.Errorf("unmarshal id failed, %w", err)
}
if len(is) != 1 {
return xerrors.Errorf("bad unmarshal id length, %d", len(is))
}
*i = ID(is[0])
return nil
}
懒惰解析/编码
有些时候我们无法一下确定需要解析的内容,可能需要先解析一部分字段,比如 type,然后才能知道另一个字段 content 的结构。这种情况下可以把 content 这个动态的结构先定义为 json.RawMessage 这个类型。
type Message struct {
MessageType MessageType `json:"messageType"`
MessageId int64 `json:"messageId"`
Payload json.RawMessage `json:"payload"`
}
如上,我们可以这样定义结构,先解析为 Message,然后根据 MessageType 准备另一个结构体去再解析一次 Payload。
m := &Message{}
err := json.Unmarshal(data, m)
if m.MessageType == "A"{
payloadA := &PayloadTypeA{}
json.Unmarshal(m.Payload, payloadA)
}else if m.MessageType == "B"{
payloadB := &PayloadTypeB{}
json.Unmarshal(m.Payload, payloadB)
}
保留长整形
默认的 json 实现中,如果一个数据是长整型,并且对应的 go 中的数据类型是 interface 的话,就会产生解析成 float64 的问题,这个是绝对不能接受的。可以通过如下办法来解决:
d := json.NewDecoder(strings.NewReader(`{"a": 1234567890987654321}`))
res1 := make(map[string]interface{})
d.Decode(&res1)
fmt.Printf("%+v, type: %s\n", res1, reflect.TypeOf(res1["a"]))
d = json.NewDecoder(strings.NewReader(`{"a": 1234567890987654321}`))
d.UseNumber()
res := make(map[string]interface{})
d.Decode(&res)
fmt.Printf("%+v, type: %s\n", res, reflect.TypeOf(res["a"]))
// output
// map[a:1.2345678909876544e+18], type: float64
// map[a:1234567890987654321], type: json.Number
参考
Read other posts