.proto 获得字段名 不贴也能解码,会显示 tag 编号 Protobuf 解码 把二进制 protobuf 消息拆成可读的 JSON。底层基于 protobufjs 解析 .proto,并配合手写的 wire format reader 做 schemaless 解码——所有处理都在浏览器本地完成,payload 和 schema 不会上传 任何服务器。
| 阶段 | 你提供 | 输出 |
|---|---|---|
| 无 schema 解码 | 仅 payload(hex / base64 / 文件) | field_<n>: value,每个 tag 给出 wire type + 多种类型解释 |
| 有 schema 解码 | payload + .proto + 选择 message 类型 | 带字段名的 JSON;.proto 没覆盖的 tag 仍以 _unknown_<n> 保留 |
切到 编码 模式可以反向操作:JSON + .proto → hex / base64,方便构造测试用例。
每个字段以 varint tag 开头:(field_number << 3) | wire_type
| wire type | 取值 | 用于 |
|---|---|---|
0 varint | int32/int64/uint32/uint64/sint32/sint64/bool/enum | 1–10 字节,最高位为 continuation bit |
1 i64 | fixed64/sfixed64/double | 固定 8 字节 |
2 len | string/bytes/embedded message/packed repeated | varint 长度 + 原始字节 |
5 i32 | fixed32/sfixed32/float | 固定 4 字节 |
3/4 | sgroup / egroup(已废弃) | proto2 早期遗留 |
int32 vs sint32:编码不同,按错的解会得到完全不一样的数字(特别是负数)。一定要按 .proto 来len 类型而不是多次重复同一 tag。无 schema 模式下会被识别为 bytes 或 messageoneof:wire 上和普通字段没区别,只是 schema 上互斥;本工具有 schema 模式会按 oneof 选中的字段输出_unknown_<n>,避免漏掉对方加的新字段Protobuf 的 wire format 自带 tag number 和 wire type——每个字段的字节流前都会编码 (field_number << 3) | wire_type,解码器据此就能拆出 "字段编号 + 大类(varint/i32/i64/length-delimited)"。看不到的只是 字段名 和 精确类型(int32 / sint32 / enum / bool 都编为 varint,分不清)。这就是 protoc --decode_raw 的原理,本工具也实现了同样的能力。
Length-delimited(wire type 2)字段在 wire format 上完全没有线索说明它是 string、bytes 还是嵌套 message。本工具按 "先递归当成 message 解,能解通就显示为 message;否则尝试 UTF-8 解码,可读则当字符串;都不行再当 bytes 给出 hex/base64" 的优先级智能猜。结果会同时给出 _wire 标记,方便你判断猜得对不对。
int32 和 sint32 解出来不一样?是的。两者编码方式不同:int32 直接用 varint 存(负数会占满 10 字节),sint32 用 ZigZag 编码(小绝对值的负数也很短)。无 schema 模式下工具把 varint 同时给出 uint、int(二补码解读)、sint(ZigZag 解读)三种值,让你按需选。有 schema 模式下 protobufjs 会按 .proto 里声明的类型自动选对。
.proto 里有 import 怎么办?暂不支持 import——浏览器拿不到外部文件。把所有依赖的 message 复制粘贴到同一份 .proto 即可。google.protobuf.Timestamp、google.protobuf.Duration、google.protobuf.Empty 等 well-known types 已被 protobufjs 内置,可直接引用。
不会。本工具会先按 .proto 解一遍,然后再无 schema 解一遍找出 schema 没覆盖的 tag,以 _unknown_<n> 为键合并到结果里。这点比直接 protoc --decode 强——后者会把未知字段静默丢弃。
JavaScript 的 Number 只能精确表达 ±2^53 范围内的整数。protobufjs 的 int64/uint64/sint64/fixed64 默认返回字符串(通过 toObject({ longs: String })),保证 18 位以内的 ID 不丢精度。你直接 JSON.parse 后还能拿到字符串原值,没问题。
gRPC over HTTP/2 的 frame 前面有 5 字节前缀:1 字节 compression flag + 4 字节 big-endian length。把这 5 字节剥掉,剩下的才是真正的 protobuf payload,再贴进本工具解码。如果 compression flag = 1,还要先按 gRPC 头里声明的 grpc-encoding(gzip 等)解压。
不能——本工具只处理 wire format(二进制)。Text format(人类可读的 field: value)和 wire format 是两种不同的序列化格式,互转要用 protoc --encode / --decode。
field_<n>: value。protobuf wire format 自带 tag number 和 wire type,所以不需要 .proto 也能拆包,只是看不到字段名和精确类型_unknown_<n> 保留import 不支持 — 把所有依赖的 message 粘到同一份 .proto 里google.protobuf.Timestamp 等)已内置常用类型,可直接引用oneof、map<K,V>、repeated、嵌套 message、enum 均可