go json的omitempty標簽導(dǎo)致protocbuf忽略默認值屬性的問題

1. json序列化會丟失默認值字段

??Go的json有一個omitempty標簽,意思是如果字段為空值,定義為false、0、零指針、nil接口值以及任何空數(shù)組、切片、映射或字符串,則該字段在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時關(guān)于json這個特性的處理

??gRPC使用Protobuf,在自動生成的代碼中,可以看到所有字段都加上了omitempty標簽。Protobuf 2版本還可以使用required關(guān)鍵字,但是Protobuf 3取消了這個關(guān)鍵字,而我們使用gRPC基本都是使用Protobuf 3。
??大佬解釋了這么設(shè)計的原因: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
??我不喜歡這些方法。其實就像上面代碼里體現(xiàn)的,go反序列化時能自動填充默認值/零值,所以只在grpc中傳遞數(shù)據(jù)時,接收方仍能正常接收到零值。如果是異構(gòu)工程,其他語言不了解,java的json庫對于基本數(shù)據(jù)類型也是取默認值的。

3. 一個實際使用中遇到的問題

??為了與前端交互,使用了grpc-gateway,不了解grpc-gateway的可以去看這里。前端使用js解析json時,不存在的屬性不能自動賦予默認值,而是得到undefined,這就要求必須傳值給前端。
??這么明顯且重要的問題當然是已經(jīng)被解決了的,使用grpc-gateway時,由:

gwmux := runtime.NewServeMux()

??修改為

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

??具體原因可以參考這里
??這里主要強調(diào)一點,grpc-gateway的工作原理簡單而言就是:

    1. 提供對外的RESTFUL API
    1. 收到請求之后,把結(jié)果編譯成proto.Message
    1. 轉(zhuǎn)發(fā)請求給GPRC
    1. 收到proto.Message之后,編譯成json,返回給調(diào)用者

??可以看到,grpc-gateway填充默認值,和gRPC填充默認值是不相關(guān)的。即使我們參考了常用解決方案,即使用sed命令替換掉protoc生成的代碼中的omitempty標簽,也只是gRPC傳輸時加上了默認值,grpc-gateway還是忽略默認值的。所以上面創(chuàng)建gwmux時的修改必不可少。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容