不知道大家在使用go实现rpc服务的时候有没有这样的困惑,因为pb文件生成的桩代码中,是自动附带 omitempty
json tag的,因此我们在序列化结构体时,如果某个字段的值是零值,则会导致序列化后的字符串不存在这个字段,而这个时候如果下游服务对反序列化要求比较严格,则会出现字段缺失的问题。或者直接访问这个字段,例如在Python中,我们很可能这样写:
response = requests.post(url, headers=headers, data=json.dumps(data))
result = response.json()
value = result["key"]
这样会出现异常:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'key'
比如下面的例子:
package main
import (
"encoding/json"
"fmt"
)
type data struct {
X string `json:"x,omitempty"`
Y string `json:"y"`
}
func main() {
d0 := &data{
X: "x",
Y: "y",
}
d1 := &data{}
sd0, _ := json.Marshal(d0)
sd1, _ := json.Marshal(d1)
fmt.Println(string(sd0), string(sd1))
}
这里打印的结果为:
{"x":"x","y":"y"} {"y":""}
由于 data.Y
使用了 omitemty
json tag,导致当 data.Y
为空时,序列化后的字符串不会含有字段 Y
。
为了解决上面的问题,我们引入第三方sdk:
package main
import (
"fmt"
"unsafe"
jsoniter "github.com/json-iterator/go"
"github.com/modern-go/reflect2"
)
var json = jsoniter.ConfigCompatibleWithStandardLibrary
type data struct {
X string `json:"x,omitempty"`
Y string `json:"y"`
}
func main() {
d0 := &data{
X: "x",
Y: "y",
}
d1 := &data{}
sd0, _ := json.Marshal(d0)
sd1, _ := json.Marshal(d1)
fmt.Println(string(sd0), string(sd1))
}
type NotOmitemptyValEncoder struct {
encoder jsoniter.ValEncoder
}
func (codec *NotOmitemptyValEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
codec.encoder.Encode(ptr, stream)
}
func (codec *NotOmitemptyValEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return false
}
type NotOmitemptyEncoderExtension struct {
jsoniter.DummyExtension
}
func (extension *NotOmitemptyEncoderExtension) DecorateEncoder(typ reflect2.Type, encoder jsoniter.ValEncoder) jsoniter.ValEncoder {
return &NotOmitemptyValEncoder{encoder: encoder}
}
func init() {
jsoniter.RegisterExtension(new(NotOmitemptyEncoderExtension))
}
执行链接在这里,打印的结果为:
{"x":"x","y":"y"} {"x":"","y":""}
可以看到符合预期,所有的字段都序列化出来了。