protobuf-net 和重复字段
Posted
技术标签:
【中文标题】protobuf-net 和重复字段【英文标题】:protobuf-net and repeated field 【发布时间】:2012-06-18 09:27:17 【问题描述】:在 protobuf 中使用 List 作为重复字段的对应项是否正确?我正在尝试这个并且总是遇到异常:
线型无效;这通常意味着您在没有截断或设置长度的情况下覆盖了文件;见Using Protobuf-net, I suddenly got an exception about an unknown wire-type
整个缓冲区是(第一个消息,下一个到达的都附加到这个): 9 8 5 26 5 24 238 98 32 1
java protobuf 文件:
package XXX;
option java_package = "XXX";
option java_outer_classname = "Protos";
option optimize_for = SPEED;
message V3DDelta
optional int32 bid = 1;
optional int32 bidSize = 2;
optional int32 ask = 3;
optional int32 askSize = 4;
message Request
optional int32 type = 1;
optional string request = 2;
message Response
optional int32 type = 1;
optional string response = 2;
repeated V3DDelta v3dDelta = 3;
和 protbuf-net 类:
[ProtoContract]
public class V3DDelta
[ProtoMember(1)]
public double bid get; set;
[ProtoMember(2)]
public int bidSize get; set;
[ProtoMember(3)]
public double ask get; set;
[ProtoMember(4)]
public int askSize get; set;
[ProtoContract]
public class Request
[ProtoMember(1)]
public int Type get; set;
[ProtoMember(2)]
public string Rq get; set;
[ProtoContract]
public class Response
[ProtoMember(1)]
public int Type get; set;
[ProtoMember(2)]
public string Rsp get; set;
[ProtoMember(3)]
public List<V3DDelta> v3dDelta get; set;
public Response()
v3dDelta = new List<V3DDelta>();
我试过 V3DDelta[] 但结果是一样的。 阅读留言:
Response rsp = Serializer.DeserializeWithLengthPrefix<Response>(rcvstream, PrefixStyle.Base128);
并且在 java 中使用 writeDelimitedTo 发送消息。 c# 中的缓冲区与 java 中的缓冲区完全相同。 当有 na v3dDelta 字段时,一切都按预期工作。
【问题讨论】:
一切顺利......这很有趣! 【参考方案1】:是的,List<T>
或数组 (T[]
) 都适用于 repeated
。顺便说一句,有一个工具可以从 .proto 定义中生成 protobuf-net 类。
您正在尝试“使用长度前缀”读取它,但是:9 无效作为 varint 前缀(9
,作为字段标题,表示“字段 1,固定的 64 位数据”,但是:在这种情况下,它应该是一个 varint)。
实际上,没有您的数据与 9
作为字段标头兼容,因为您在定义为字段 1
时没有任何 64 位值。您确实有一个double
作为字段3
,它可以很好地完成这项工作 - 但是,它将73
作为字段标题。
我会告诉你序列 9,8,5,... 代表什么,但是 - 我们会:
9 : field 1, fixed 64-bit
8 5 26 5 24 238 98 32 <== payload for above
1 : field 0, fixed 64-bit
^^ not *really* valid, but fields <= 0 generally mean "stop" - but
frankly this is not a clean/defined/expected exit condition
再说一遍:请检查您的数据。这看起来不像一个 protobuf 流,或者至少不是一个与您的架构匹配的流。
编辑:可能 java writeDelimitedTo 包含 只是 长度,没有标题)使其在技术上不是一致的 protobuf 文件,但是......嗯),所以让我们调查一下:
int len = ProtoReader.DirectReadVarintInt32(ms);
这给了我们9
,我们还剩下9
字节,所以看起来不错......
8 : Field 1, varint
5 = payload of above
26 : Field 3, length-delimited
5 = length of payload
24 238 98 32 1 = payload of ^^^
24 : Field 3, varint
238, 98 = payload of ^^^ = 12654
32 : Field 4, varint
1 = payload of ^^^ = 1
现在看起来它应该解析...调查为什么它不是...
编辑 2:经过更多调试,部分是因为您(很抱歉)破坏了 V3DDelta
属性。您在 proto 中将它们定义为 int32
(并且字段 3 是数据中的 varint),但您将它们实现为 double
... 和 double
和 int32
不是朋友。
所以:
[ProtoContract]
public class V3DDelta
[ProtoMember(1)]
public int bid get; set;
[ProtoMember(2)]
public int bidSize get; set;
[ProtoMember(3)]
public int ask get; set;
[ProtoMember(4)]
public int askSize get; set;
然后以下工作正常:
using (var ms = new MemoryStream(buffer))
int len = ProtoReader.DirectReadVarintInt32(ms);
var resp = (Response)model.Deserialize(ms, null, typeof(Response), len);
Assert.AreEqual(5, resp.Type);
Assert.AreEqual(1, resp.v3dDelta.Count);
Assert.AreEqual(12654, resp.v3dDelta[0].ask);
Assert.AreEqual(1, resp.v3dDelta[0].askSize);
从技术上讲,我可以让 protobuf-net 接受 varint
以获得 double
值,但这高度表明架构并不真正匹配,所以我认为正确在这种情况下,事情是改变类型。
【讨论】:
是的,就是这样,现在它可以正常工作了。我只是在 proto 中更改字段类型后忘记了更改字段类型。谢谢您,很抱歉占用您的时间。以上是关于protobuf-net 和重复字段的主要内容,如果未能解决你的问题,请参考以下文章
Protobuf-net / NetCore2:反序列化忽略带注释的私有字段