美文网首页
go json的omitempty标签导致protocbuf忽略

go json的omitempty标签导致protocbuf忽略

作者: 猫尾草 | 来源:发表于2020-07-12 16:55 被阅读0次

    1. json序列化会丢失默认值字段

      Go的json有一个omitempty标签,意思是如果字段为空值,定义为false、0、零指针、nil接口值以及任何空数组、切片、映射或字符串,则该字段在json序列化时省略。
    例如下面一段代码:

    package main
    
    import (
        "encoding/json"
        "log"
    )
    
    type Asdqwe struct {
        A int32    `json:"a,omitempty"`
        B bool     `json:"b,omitempty"`
        C [0]int32 `json:"c,omitempty"`
        D []int32  `json:"d,omitempty"`
    }
    
    func main() {
        testStruct := Asdqwe{A: 1}
        log.Printf("%v\n", testStruct)
        res, err := json.Marshal(testStruct)
        if err != nil {
            log.Fatal(err)
        }
        log.Println(string(res))
        testStruct2 := Asdqwe{}
        err = json.Unmarshal(res, &testStruct2)
        log.Printf("%v", testStruct2)
    }
    

    输出:

    2020/07/13 14:36:18 {1 false [] []}
    2020/07/13 14:36:18 {"a":1}
    2020/07/13 14:36:18 {1 false [] []}
    

      可以看到,json序列化之后,除了赋值的A,其他B、C、D都没有了。但是,使用json反序列化后,重新赋予了默认值。所以这里问题不大。

    2. grpc使用Protobuf时关于json这个特性的处理

      gRPC使用Protobuf,在自动生成的代码中,可以看到所有字段都加上了omitempty标签。Protobuf 2版本还可以使用required关键字,但是Protobuf 3取消了这个关键字,而我们使用gRPC基本都是使用Protobuf 3。
      大佬解释了这么设计的原因:https://stackoverflow.com/questions/31801257/why-required-and-optional-is-removed-in-protocol-buffers-3
      你如果一定要让零值存在于json序列化后的字符串中,这里有一些解决办法:https://zhuanlan.zhihu.com/p/46603988
      一个使用开源库来增加protoc特性的方法:https://studygolang.com/articles/28563?fr=sidebar
      我不喜欢这些方法。其实就像上面代码里体现的,go反序列化时能自动填充默认值/零值,所以只在grpc中传递数据时,接收方仍能正常接收到零值。如果是异构工程,其他语言不了解,java的json库对于基本数据类型也是取默认值的。

    3. 一个实际使用中遇到的问题

      为了与前端交互,使用了grpc-gateway,不了解grpc-gateway的可以去看这里。前端使用js解析json时,不存在的属性不能自动赋予默认值,而是得到undefined,这就要求必须传值给前端。
      这么明显且重要的问题当然是已经被解决了的,使用grpc-gateway时,由:

    gwmux := runtime.NewServeMux()
    

      修改为

    gwmux := runtime.NewServeMux(runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{OrigName: true, EmitDefaults: true}))
    

      具体原因可以参考这里
      这里主要强调一点,grpc-gateway的工作原理简单而言就是:

      1. 提供对外的RESTFUL API
      1. 收到请求之后,把结果编译成proto.Message
      1. 转发请求给GPRC
      1. 收到proto.Message之后,编译成json,返回给调用者

      可以看到,grpc-gateway填充默认值,和gRPC填充默认值是不相关的。即使我们参考了常用解决方案,即使用sed命令替换掉protoc生成的代码中的omitempty标签,也只是gRPC传输时加上了默认值,grpc-gateway还是忽略默认值的。所以上面创建gwmux时的修改必不可少。

    相关文章

      网友评论

          本文标题:go json的omitempty标签导致protocbuf忽略

          本文链接:https://www.haomeiwen.com/subject/rqqzcktx.html