使用 C# Google.Protobuf 将 ByteString 反序列化为对象

Posted

技术标签:

【中文标题】使用 C# Google.Protobuf 将 ByteString 反序列化为对象【英文标题】:Deserializing ByteString into an object using C# Google.Protobuf 【发布时间】:2020-03-03 11:00:30 【问题描述】:

所以我有一条来自不同服务的 gRPC 消息(用另一种编程语言编写)。 这是这个对象的一个​​迷你版(很明显真实的是由protobuf生成的)

public class Message

    public string Topic  get; set; 
    public string Identifier  get; set; 
    public Google.Protobuf.ByteString Msg  get; set; 

我的问题是应该将 ByteString 反序列化为另一个对象(也在 protobuf 文件中定义),但是当我尝试反序列化 Msg 字段时,我不断收到此错误:

协议消息包含带有无效线路类型的标签。

和堆栈跟踪

在 Google.Protobuf.UnknownFieldSet.MergeFieldFrom(CodedInputStream 输入)在 Google.Protobuf.UnknownFieldSet.MergeGroupFrom(CodedInputStream 输入) 在 Google.Protobuf.CodedInputStream.ReadGroup(Int32 fieldNumber, UnknownFieldSet 集)在 Google.Protobuf.UnknownFieldSet.MergeFieldFrom(CodedInputStream 输入) 在 Google.Protobuf.UnknownFieldSet.MergeFieldFrom(UnknownFieldSet unknownFields,CodedInputStream 输入)在 Messages.RecordingStatusChangeMes​​sage.MergeFrom(CodedInputStream 输入)在 C:\Users\iliaar\go\src\RecorderApp\testers\NewRecorderTester\Infra.AppDataManager\Model\Protos\Pubsub\messages.pb.cs:line 3259 在 ClusterRecordersModule.RecordersViewModel.OnNewMessage(消息 消息)在 C:\Users\iliaar\go\src\RecorderApp\testers\NewRecorderTester\ClusterRecordersModule\ViewModels\RecordersViewModel.cs:line 154

我尝试调用新对象的几个方法,但都失败了 例如,我尝试使用现有实例并合并如下:

innerMessageObject.MergeFrom(message.Msg.CreateCodedInput());

或使用静态解析器,如:

InnerMessageObject.Parser.ParseFrom(message.Msg.ToByteArray());

所有都因相同的错误而失败,并且堆栈跟踪最终收敛到 Google.Protobuf 库中的同一位置。 我的库版本是 8.1.0,我还尝试一直降级到版本 6

我真的很感激一些帮助。 谢谢

编辑: innerMessage 对象的结构如下:

public class InnerMessageObject

    public string ConfigID  get; set; 
    public bool Storage  get; set; 
    public bool Signal  get; set; 
    public string StorageTransition  get; set; 
    public string SignalTransition  get; set; 

另外,转换为十六进制会导致以下(有效)输出

63-6F-6E-66-69-67-5F-69-64-3A-20-37-32-30-62-66-65-34-39-2D-64-62-32-39-2D-34-35-38-33-2D-39-66-65-31-2D-65-30-32-30-37-33-32-37-39-37-39-34-0A-73-74-6F-72-61-67-65-3A-20-66-61-6C-73-65-0A-73-69-67-6E-61-6C-3A-20-66-61-6C-73-65-0A-73-74-6F-72-61-67-65-5F-74-72-61-6E-73-69-74-69-6F-6E-3A-20-22-32-30-32-30-2D-30-33-2D-30-33-54-31-32-3A-32-36-3A-34-31-2E-33-32-37-34-30-34-33-5A-22-0A-73-69-67-6E-61-6C-5F-74-72-61-6E-73-69-74-69-6F-6E-3A-20-22-32-30-32-30-2D-30-33-2D-30-33-54-31-32-3A-32-36-3A-34-31-2E-33-32-37-34-30-33-32-33-31-5A-22-0A

此外,将字节数组转换为字符串会导致具有正确数据的对象的字符串表示

var ba = pubsubMessage.Msg.ToByteArray();
return Encoding.UTF8.GetString(ba);

config_id: 720bfe49-db29-4583-9fe1-e02073279794 存储:假 信号:假 storage_transition:“2020-03-03T12:29:59.531473957Z” 信号转换:“2020-03-03T12:29:59.531473589Z”

【问题讨论】:

这里真正的问题是:Msg 的有效载荷真的有效吗?我猜它不是,但要对此进行测试,请将有效负载内容获取为十六进制:var hex = BitConverter.ToString(message.Msg.ToByteArray())(并在某处写入hex - 到控制台很好),然后通过@987654321 运行该十六进制@ - 如果验证者同意数据无效:数据无效,你需要做的是修复数据的来源 我认为内容很好,因为在源服务中反序列化它可以正常工作。十六进制验证器也没有指定任何错误 那很奇怪;我不知道您的数据/有效负载在这里有多敏感;您能否分享InnerMessageObject 的有效负载和架构,以便我们提供更多帮助? (上下文:我是那个验证器站点的作者;我非常了解协议) 是的,我现在正在编辑主要问题 【参考方案1】:

您的有效负载不是 protobuf。如果我们通过this validator 进行尝试,我们会看到:

63 = 字段 12,键入 StartGroup

错误:线型无效;这通常意味着您在没有截断或设置长度的情况下覆盖了文件;见Using Protobuf-net, I suddenly got an exception about an unknown wire-type

所以让我们看一下前两个字节,就好像它们是 protobuf 一样——解码器告诉我们 0x63 是什么意思;下一个字节 0x6F 应该是字段头(“标签”);在二进制中,这是 01101111,即“字段 13,电线类型 7”; protobuf 中没有 7 号线。所以;解码器正确:有效载荷无效。

但并非全部丢失!

如果我们从所有这些 0x6* 值中猜测这可能是 ASCII 或 UTF8,并对其进行解码,我们会得到:

config_id: 720bfe49-db29-4583-9fe1-e02073279794
storage: false
signal: false
storage_transition: "2020-03-03T12:26:41.3274043Z"
signal_transition: "2020-03-03T12:26:41.327403231Z"

这似乎是您的一些粗略的基于行的标记化格式的数据。但是:不是protobuf。

【讨论】:

我想你是对的,问题是其他服务是用 golang 编写的,我使用 proto.Marshal 来序列化对象,当我在 go 服务中再次反序列化它时它工作正常。有没有可能与语言差异有关? @Nitzanu no;无论 golang 正在做什么来构建有效负载,它都不会创建 protobuf 输出;如果它正在创建 protobuf 输出,那么无论是哪种语言,它都是 protobuf

以上是关于使用 C# Google.Protobuf 将 ByteString 反序列化为对象的主要内容,如果未能解决你的问题,请参考以下文章

用于 C# 的 Google Protobuf 3.0.0 程序集

通讯协议及Google.Protobuf生成c#代码 序列及反序列化

Protobuf 从另一个 proto C# 项目导入消息

google protobuf 数据类型_理解Protobuf数据格式解析

Google Protobuf简明教程

基于google protobuf的gRPC实现