无.proto文件时如何解码Google Protobuf响应为可读格式?
解决方案:无.proto文件解码Protobuf二进制数据
没有.proto文件解码Protobuf确实有点棘手,但好在有几个实用的工具和方法能帮你搞定,下面给你两种靠谱的方案:
方法一:用protobuf-inspector快速解析(推荐)
这个第三方库专门针对无.proto文件的场景设计,能直接解析二进制Protobuf数据,输出结构化的可读格式,还能转成字典方便后续处理。
步骤1:安装依赖
先通过pip安装库:
pip install protobuf-inspector
步骤2:修改你的RabbitMQ消费代码
把回调函数改成下面这样,就能自动解析并输出可读内容和字典格式:
import pika from protobuf_inspector import inspect_message from protobuf_inspector.types import StandardParser credentials = pika.PlainCredentials('demo', 'demo') cp = pika.ConnectionParameters( host='127.0.0.1', port=5671, credentials=credentials, ssl=False, ) connection = pika.BlockingConnection(cp) channel = connection.channel() def callback(ch, method, properties, body): print(" [x] Raw received body: %r" % body) # 解析Protobuf二进制数据 parser = StandardParser() parsed_msg = inspect_message(body, parser) print("\n [x] Parsed readable format:") print(parsed_msg) # 把解析结果转成字典(方便业务处理) def parsed_to_dict(msg): result = {} for field in msg.fields: key = str(field.tag.field_number) # 处理嵌套消息 if hasattr(field.value, 'fields'): result[key] = parsed_to_dict(field.value) # 处理重复字段 elif isinstance(field.value, list): result[key] = [item.value for item in field.value] # 基础类型直接取值 else: result[key] = field.value return result msg_dict = parsed_to_dict(parsed_msg) print("\n [x] Converted to dictionary:") print(msg_dict) channel.basic_consume(callback, queue='demo_queu', no_ack=True) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming()
运行这段代码后,针对你提供的二进制数据,会输出类似这样的结构化结果(简化版):
1: 0 2: { 1: 450010 2: 240054 3: 123456789 4: 0 ... } ...
方法二:手动解析(当第三方库失效时)
如果遇到复杂的嵌套结构或者第三方库解析不准确的情况,可以基于Protobuf的编码规则手动解析。Protobuf的每个字段由tag和value组成,tag包含字段号和wire type:
- wire type 0:Varint类型(整数、布尔值等)
- wire type 2:Length-delimited类型(字符串、嵌套消息、字节数组等)
你可以用官方库的基础工具实现解析:
import pika from google.protobuf.internal.decoder import _DecodeVarint32 credentials = pika.PlainCredentials('demo', 'demo') cp = pika.ConnectionParameters( host='127.0.0.1', port=5671, credentials=credentials, ssl=False, ) connection = pika.BlockingConnection(cp) channel = connection.channel() def parse_protobuf(data): result = {} pos = 0 while pos < len(data): # 解析字段tag tag, pos = _DecodeVarint32(data, pos) field_num = tag >> 3 wire_type = tag & 0x7 if wire_type == 0: # 解析Varint类型值 value, pos = _DecodeVarint32(data, pos) result[field_num] = value elif wire_type == 2: # 解析Length-delimited类型,先读长度再读内容 length, pos = _DecodeVarint32(data, pos) sub_data = data[pos:pos+length] pos += length # 尝试递归解析嵌套消息,失败则存原始十六进制字符串 try: result[field_num] = parse_protobuf(sub_data) except: result[field_num] = sub_data.hex() return result def callback(ch, method, properties, body): print(" [x] Raw received body: %r" % body) msg_dict = parse_protobuf(body) print("\n [x] Parsed dictionary:") print(msg_dict) channel.basic_consume(callback, queue='demo_queu', no_ack=True) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming()
这种方法虽然没有字段名,但能完整解析出所有字段的结构和值,适合调试或自定义处理场景。
内容的提问来源于stack exchange,提问作者aaa
相关产品推荐
相关产品推荐

