|
发表于 2025-7-25 00:41:55
|
显示全部楼层
浙江省湖州市
在处理PB(Protocol Buffers)格式数据转JSON时出现乱码、解析失败的问题,核心原因通常是**数据格式识别错误**(如PB被错误编码)、**缺少.proto定义文件**或**中间件额外处理**(如压缩、加密)导致的。以下是分步骤解决方案:
### 一、问题根源分析
1. **PB是二进制格式,非文本编码**
PB本身是二进制协议,并非文本格式(如JSON、XML),直接用UTF-8、GBK等编码解码会导致乱码(表现为“口”字符或特殊符号)。Sun中间件的响应中“乱码+正常文本”可能是:
- 部分字段是PB二进制数据,部分是文本(如混合协议)。
- PB数据被Base64编码或压缩(如gzip),未先解码/解压。
2. **缺少.proto文件无法解析结构**
PB需要对应的`.proto`定义文件(描述字段类型、名称、顺序)才能正确解析。没有`.proto`时,即使数据正确,也无法映射为JSON字段(只能看到二进制流)。
3. **中间件的额外处理**
Sun中间件可能对PB数据做了包装(如加协议头、加密),直接解析原始响应会失败。
### 二、解决方案:分步骤解析PB数据
#### 步骤1:确认数据是否为原始PB(或经过编码)
首先判断抓包得到的响应数据是否为原始PB,还是经过处理(如Base64):
- **原始PB**:用十六进制查看器(如HxD)打开,开头通常是`08`、`12`等PB字段标签(见[PB编码规则](https://developers.google.com/protocol-buffers/docs/encoding))。
- **Base64编码的PB**:数据由字母、数字、`+`、`/`组成,可先用Base64解码为二进制。
- **压缩的PB**:若数据开头是`1f8b`(gzip标识),需先解压。
**示例(Python判断并预处理)**:
```python
import base64
import gzip
from io import BytesIO
def preprocess_pb_data(raw_data):
# 尝试Base64解码
try:
raw_data = base64.b64decode(raw_data)
print("数据经过Base64编码,已解码")
except:
pass
# 尝试gzip解压
try:
# 检查gzip标识
if raw_data.startswith(b'\x1f\x8b'):
raw_data = gzip.GzipFile(fileobj=BytesIO(raw_data)).read()
print("数据经过gzip压缩,已解压")
except:
pass
return raw_data
```
#### 步骤2:获取或逆向`.proto`文件
解析PB必须有`.proto`文件。若没有,可通过以下方式获取:
1. **官方文档**:查询Sun中间件或接口提供方的API文档,通常会附带`.proto`定义。
2. **逆向推测**:若已知部分字段含义(如“正常文本”对应的字段),可通过[pbinspector](https://github.com/jaqx0r/pbinspector)等工具逆向推测`.proto`结构(准确率有限)。
**示例`.proto`文件(假设结构)**:
```protobuf
syntax = "proto3";
message SunResponse {
string normal_text = 1; // 正常文本字段
bytes pb_data = 2; // 二进制PB子字段(可能包含乱码)
int32 code = 3; // 状态码
}
```
#### 步骤3:用protobuf库解析为JSON
有了`.proto`文件后,用对应语言的protobuf库解析二进制数据,再转JSON:
**Python实现示例**:
1. 安装依赖:`pip install protobuf`
2. 将`.proto`编译为Python代码:
```bash
protoc --python_out=. sun_response.proto # 生成sun_response_pb2.py
```
3. 解析PB数据并转JSON:
```python
import sun_response_pb2 # 导入编译后的模块
import json
# 1. 预处理数据(解压/Base64解码,见步骤1)
raw_data = b"...") # 抓包得到的原始响应数据
processed_data = preprocess_pb_data(raw_data)
# 2. 解析PB
response = sun_response_pb2.SunResponse()
response.ParseFromString(processed_data) # 关键:用PB库解析二进制
# 3. 转JSON(需处理bytes字段,避免乱码)
def pb_to_json(pb_msg):
json_dict = {}
for field in pb_msg.DESCRIPTOR.fields:
value = getattr(pb_msg, field.name)
# 处理bytes类型(避免直接转字符串导致乱码)
if field.type == field.TYPE_BYTES:
json_dict[field.name] = f"base64:{base64.b64encode(value).decode()}"
else:
json_dict[field.name] = value
return json.dumps(json_dict, ensure_ascii=False, indent=2)
json_result = pb_to_json(response)
print(json_result)
```
#### 步骤4:处理混合格式(文本+PB)
若响应中部分是文本、部分是PB(如“正常文本+乱码”),可通过以下方式分离:
1. 用十六进制定位文本与PB的分隔点(文本通常以`00`或换行结束,PB以字段标签开头)。
2. 分别解析:文本部分直接用对应编码(如UTF-8)解码,PB部分按步骤3处理。
#### 步骤5:解决“口”字符等乱码
- 若PB中`bytes`类型字段被错误当作字符串解码(如用UTF-8),会出现“口”字符,需在转JSON时单独处理(如Base64编码后存储,见步骤3的`pb_to_json`函数)。
- 若字段本身是字符串但编码错误(如PB用GBK,解析时用了UTF-8),可尝试指定编码:
```python
str_value = value.decode('gbk', errors='replace') # 替换错误字符
```
### 三、工具推荐:简化解析流程
1. **PB解析工具**:
- [Protobuf Decoder](https://protobuf-decoder.net/):在线解析(需已知`.proto`结构)。
- [WireShark](https://www.wireshark.org/):安装PB插件后,可直接在抓包中解析PB数据(需导入`.proto`)。
2. **编码转换工具**:
- [HxD](https://mh-nexus.de/en/hxd/):查看十六进制数据,判断是否为Base64、gzip。
- [Online Encoder](https://base64.guru/):批量测试Base64/UTF-8/GBK编码转换。
### 总结
核心流程:**预处理(解压/解码)→ 用.proto解析PB → 针对性处理bytes字段→转JSON**。关键是明确:PB是二进制协议,不能直接用文本编码解析,必须依赖`.proto`定义和专用库。若缺少`.proto`,需先逆向或向提供方获取,否则无法正确映射字段含义。 |
|