没有干净的方法,但有一些 hacky 方法。一种是像这样定义第二条消息:
message RawOuter {
repeated bytes inners = 1;
// ... same fields as Outer ...
}
RawOuter
Outer
除了inners
重复字段已从 type 更改为 typeInner
之外,与 相同bytes
。如果您inners
使用 的编码实例进行填充Inner
,然后对 进行序列化,您将获得与使用已解析的版本RawOuter
构建一个完全相同的结果。Outer
也就是说,嵌套消息的有线格式与bytes
包含该嵌套消息的序列化的字段的有线格式相同。这是 protobuf 编码的可利用怪癖之一。
不过,这个 hack 有一些问题。特别是,如果您尝试构建一个嵌入在其他原型中的实例,它就不能很好地工作Outer
,因为您可能不想维护每个包含消息的两个副本,一个 usingOuter
和一个 using RawOuter
。
另一个更老套的选择是将编码的消息注入Outer
实例的UnknownFieldSet
.
Outer outer;
for (auto& inner: inners) {
outer.mutable_unknown_fields()
->AddLengthDelimited(1, inner);
}
旨在存储解析时看到的与文件中UnknownFieldSet
定义的任何已知字段编号不匹配的字段。.proto
这个想法是,这允许您编写一个代理服务器,它只接收消息并将它们转发到另一个服务器,而无需在每次向协议中添加新字段时重新编译代理。在这里,我们通过在其中插入一个实际上对应于已知字段的值来滥用它,但实现不会注意到,因此它会很好地写出这些字段。
这种方法的主要问题是,如果其他人Outer
同时检查您的实例,在他们看来,列表好像inners
是空的,因为这些值实际上隐藏在其他地方。这是一个非常丑陋的黑客,以后可能会回来困扰你。如果您测量了性能差异并发现它很大,我只会推荐它。
另请注意,序列化代码总是最后写入未知字段,而已知字段按字段编号顺序写入。解析器应该接受任何顺序,但有时您会发现有人将未解析的数据用作哈希映射键或其他东西,如果重新排序字段,则会完全中断。
顺便说一句,您可以通过将字符串交换到位而不是复制来提高这两种方法的性能,即
raw_outer->add_inners()->swap(inner);
或者
outer->mutable_unknown_fields()->AddLengthDelimited(1)->swap(inner);