GRPC-gateway(Golang):如何在JSON响应中实现部分字段返回null、部分字段为空时省略
GRPC-gateway(Golang):如何在JSON响应中实现部分字段返回null、部分字段为空时省略
我完全理解你的困扰——既要让某些字段在为空时直接从JSON响应里消失,又要让另一些字段哪怕是空值也必须显示null,还不能修改现有的Proto结构,默认的JSONPb配置根本没法同时满足这两个需求对吧?
别担心,我们可以通过自定义JSON marshaler来解决这个问题,不需要改动任何Proto定义,只需要在Go代码里做一层包装处理就行。
核心思路
默认的JSONPb要么开启EmitUnpopulated: true(所有未设置的字段都输出null),要么关闭它(所有未设置的字段都省略),没法针对单个字段做差异化处理。我们的解决办法是:
- 先开启
EmitUnpopulated: true序列化,确保需要显示null的字段(也就是nullableValue)能正常输出null; - 序列化完成后,手动修改JSON结果,把需要省略的字段(
reallyOptionalValue)如果是null或者空字符串的话,从JSON对象中移除。
代码实现
首先,我们定义一个自定义的marshaler,嵌入原生的runtime.JSONPb,然后重写它的Marshal方法:
import ( "encoding/json" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "google.golang.org/protobuf/encoding/protojson" // 导入你的proto生成的包,比如: // pb "your/project/path/proto" ) type CustomJSONMarshaler struct { runtime.JSONPb } func (m *CustomJSONMarshaler) Marshal(v interface{}) ([]byte, error) { // 先用默认的JSONPb规则序列化(开启EmitUnpopulated,保证nullableValue输出null) rawData, err := m.JSONPb.Marshal(v) if err != nil { return nil, err } // 只处理我们关心的Response类型,其他类型直接返回原始结果 resp, ok := v.(*pb.Response) if !ok { return rawData, nil } // 把JSON解析成map,方便修改字段 var jsonObj map[string]interface{} if err := json.Unmarshal(rawData, &jsonObj); err != nil { return nil, err } // 处理reallyOptionalValue:如果值是null或者空字符串,就从JSON中删掉 if val, exists := jsonObj["reallyOptionalValue"]; exists { if val == nil || val == "" { delete(jsonObj, "reallyOptionalValue") } } // 把修改后的map重新序列化为JSON return json.Marshal(jsonObj) }
然后,在创建GRPC-Gateway的mux时,使用这个自定义的marshaler:
func main() { // 初始化自定义marshaler customMarshaler := &CustomJSONMarshaler{ JSONPb: runtime.JSONPb{ MarshalOptions: protojson.MarshalOptions{ EmitUnpopulated: true, // 确保nullableValue未设置时输出null UseCamelCase: true, // 把proto的下划线字段名转成JSON的驼峰命名 }, }, } // 创建mux并指定自定义marshaler mux := runtime.NewServeMux( runtime.WithMarshalerOption(runtime.MIMEWildcard, customMarshaler), ) // 后续的GRPC-Gateway注册逻辑... }
效果验证
- 当
really_optional_value未设置或者为空字符串时,JSON响应里不会出现reallyOptionalValue字段; - 当
nullable_value未设置时,JSON响应里会显示"nullableValue": null; - 两个字段都有有效值时,会正常输出对应的字符串。
这个方案完全不需要修改你的Proto结构,完美适配你的需求~
内容来源于stack exchange
相关产品推荐
相关产品推荐

