Protobuf.net 如何反序列化 int 数组(打包数据)

问题描述 投票:0回答:1

我们正在使用 protobuf 来使多个系统相互通信。其中一些使用 protobuf.net,一些使用其他语言和库。 我们的应用程序级消息由标头和正文组成。

在 .net 世界中,我们有:

public class ProtobufMessage
{
    public IList<byte[]> Frames { get; }
}
    
RuntimeModel = RuntimeTypeModel.Create();
RuntimeModel.Add(typeof(ProtobufMessage), false).Add(1, nameof(ProtobufMessage.Frames));

(由于消息为 false:类似元组的类型不支持此操作。要禁用类似元组的类型发现,请在首次将类型添加到模型时使用 applyDefaultBehaviour: false。)

当消息到达时,我们获取 header Frame,将其反序列化,现在我们可以获取正文的类型来反序列化下一个 Frame (byte[]):

byte[] bytes; // Body Frame
using var memory = new MemoryStream(bytes);
return typeModel.Deserialize<T>(memory); // T: type from the Header frame

当数据类型是类时,一切正常。
但有时,主体数据只是

repeated int32
,因此,没有将原型合约添加到 RuntimeModel。

假设我们发送 [1,2,3] 作为正文(标签 1) 当数据到达时:080108020803(解压)主体帧解码正常
在 proto3 中,标量数字类型的重复字段默认使用压缩编码。

但是当 protobuf-net 尝试反序列化时:0a03010203 抛出异常:无效的线路数据(字符串)

我们做错了什么吗? 当没有类来装饰时,如何告诉 protobuf 传入的数据已打包?

c# deserialization protocol-buffers protobuf-net
1个回答
0
投票

感觉有点像GPB没有被正确使用。在 GPB 中,您不需要先发送一条“标头”消息来告诉您下一条消息是什么; GPB 提供了这种“下一步是什么?”的信息。情况;这就是

oneof
的用途。关键字
oneof
是一种自我描述;它包含“以下之一”。

如果有一系列消息类型可以从一个地方发送到另一个地方,最好的办法是将所有这些类型合并到带有

oneof
字段的单个消息中。

message A
{
..
}

message B
{
..
}

message MessageWrapper
{
    oneof msg
    {
        A a = 1;
        B b = 2;
    }
}

这个想法是,所有您序列化/发送/接收/反序列化的都是 MessageWrappers,反序列化后,您询问 MessageWrapper 对象它包含的一个的变体。

副作用是您有一个针对任何消息类型的类。

关于如何处理

repeated int32
,我认为通过检查使用
.proto
编译描述消息的
protoc
文件会发生什么,可以学到很多东西。您最终不会得到独立的标量类型,您最终会得到一个以标量类型作为成员的 C# 类。我认为这是一个很大的暗示。

此外,是否有任何特殊原因要先编写项目代码,然后在运行时构建?您说您有多种语言的多个系统。最简单的事情,实际上是 Google 创建 GPB 的具体原因之一,就是拥有一个 .proto 文件,您可以将其编译为 c#、java、c++,无论您的项目需要什么。 .proto 文件是整个项目的“单一事实点”,项目的所有成员都接受该文件,对其进行编译,保证成功的互操作性。您似乎正在采取的方法 - 用 C# 编写类并装饰它们,以便它们可以序列化为 GPB 有线格式 - 意味着

  • 重复工作 - 你也必须用其他语言来做这件事
  • 存在误解的风险 - 您可能不会以与其他语言的其他人完全相同的方式编写课程
  • 您无法轻松更新系统之间传递的消息内容,因为每个人都必须重写才能做到这一点。

最好有一个 .proto 文件并编译它。

© www.soinside.com 2019 - 2024. All rights reserved.