You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让gRPC响应返回指定的简洁JSON结构?

使用Protobuf Struct类型时,gRPC响应格式不符合预期的问题

我在使用Protobuf的Struct类作为datasets类型时遇到格式问题,需要返回简洁的JSON响应给REST客户端,但实际得到的是带有fields、structValue等嵌套包装字段的格式。

Proto定义

import "google/protobuf/struct.proto";

message Datasets {
  string meta_field = 1;
  google.protobuf.Struct datasets = 2;
}

Python服务端创建对象代码

ds = example_pb2.Datasets(meta_field = "lorem")

ds.datasets['person'] = { 
   'fname':'foo', 
   'lname':'bar',  
   'addresses': [
       {
           'number': 12, 
           'post_code': 'ABC'
       },
       {
           'number': 33, 
           'post_code': 'XYZ'
       } 
    ],
    ...
}

注:addresses是动态字段,未在proto文件中定义对应的message

预期响应格式

{
  "meta_field": "lorem",
  "datasets": {
    "fname": "foo",
    "lname": "bar",
    "addresses": [
      {
        "number": 12,
        "post_code": "ABC"
      },
      {
        "number": 33,
        "post_code": "XYZ"
      }
    ]
  }
}

实际得到的响应格式

{
  "meta_field": "lorem",
  "datasets": {
    "fields": {
      "person": {
        "structValue": {
          "fields": {
            "fname": {
              "stringValue": "foo",
              "kind": "stringValue"
            },
            "addresses": {
              "listValue": {
                "values": [
                  {
                    "structValue": {
                      "fields": {
                        "post_code": {
                          "stringValue": "ABC",
                          "kind": "stringValue"
                        },
                        "number": {
                          "numberValue": 12,
                          "kind": "numberValue"
                        }
                      }
                    },
                    "kind": "structValue"
                  },
                  {
                    "structValue": {
                      "fields": {
                        "post_code": {
                          "stringValue": "XYZ",
                          "kind": "stringValue"
                        },
                        "number": {
                          "numberValue": 33,
                          "kind": "numberValue"
                        }
                      }
                    },
                    "kind": "structValue"
                  }
                ]
              },
              "kind": "listValue"
            },
            "lname": {
              "stringValue": "bar",
              "kind": "stringValue"
            }
          }
        },
        "kind": "structValue"
      }
    }
  }
}

尝试改用map<string, google.protobuf.ListValue>后结果仍一致。现在服务要被REST API调用,客户端要求返回预期的简洁格式,请问能否实现?我遗漏了什么?


解决方案

这是因为Protobuf默认的JSON序列化会严格遵循Struct的内部定义输出包装字段,你需要使用Protobuf专门提供的JSON转换工具来生成简洁格式:

1. Python端直接序列化时使用专用工具

不要用普通的json.dumps(),改用google.protobuf.json_format模块,它会自动处理Struct类型的规范化JSON输出:

from google.protobuf import json_format

# 将Datasets对象序列化为简洁JSON
response_json = json_format.MessageToJson(
    ds,
    preserving_proto_field_name=True,  # 保持与proto定义一致的字段名
    including_default_value_fields=False,  # 不输出默认值字段
    indent=2  # 格式化输出便于阅读
)

2. 若使用gRPC-Gateway暴露REST接口

确保你使用的是新版本的gRPC-Gateway,它默认会对Struct、ListValue等Well-Known类型进行规范化JSON转换,无需额外配置。如果是老版本,可检查proto中的HTTP注解配置是否正确:

import "google/api/annotations.proto";

service YourService {
  rpc GetDatasets(...) returns (Datasets) {
    option (google.api.http) = {
      get: "/v1/datasets"
      response_body: "*"
    };
  }
}

3. 为什么之前的尝试无效?

map<string, google.protobuf.ListValue>本质还是依赖Protobuf的Well-Known类型定义,默认序列化依然会保留内部包装字段。只有使用专门处理Protobuf JSON的工具,才能将这些类型转换为符合直觉的简洁JSON结构。


内容的提问来源于stack exchange,提问作者Bilal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 12:54:28