使用 MonoTouch 反序列化 Protobuf 文件的问题

Posted

技术标签:

【中文标题】使用 MonoTouch 反序列化 Protobuf 文件的问题【英文标题】:Issue Deserializing Protobuf files with MonoTouch 【发布时间】:2012-10-18 13:20:49 【问题描述】:

我们目前面临的问题是,我们希望使用 MonoTouch 将当前在 android (Monodroid) 和 Wp7 上运行的应用程序移植到 ios

这不是问题,但是使用 protobuf-net 框架反序列化数据总是会失败,并出现以下异常:

ProtoBuf.ProtoException: Invalid wire-type; this usually means you have over-written a file without truncating or setting the length; see http://***.com/q/2152978/23354
  at ProtoBuf.ProtoReader.StartSubItem (ProtoBuf.ProtoReader reader) [0x00000] in <filename unknown>:0
  at ProtoBuf.BclHelpers.ReadGuid (ProtoBuf.ProtoReader source) [0x00000] in <filename unknown>:0
  at TpSerializer.Read (Application.Mobile.TpDataAccess.TimeEntryLoggingDao.Entities.ActiveTimeEntryDbo , ProtoBuf.ProtoReader ) [0x00000] in <filename unknown>:0
  at TpSerializer.Deserialize (Int32 , System.Object , ProtoBuf.ProtoReader ) [0x00000] in <filename unknown>:0
  at ProtoBuf.Meta.TypeModel.TryDeserializeAuxiliaryType (ProtoBuf.ProtoReader reader, DataFormat format, Int32 tag, System.Type type, System.Object& value, Boolean skipOtherFields, Boolean asListItem, Boolean autoCreate, Boolean insideList) [0x00000] in <filename unknown>:0
  at ProtoBuf.Meta.TypeModel.TryDeserializeList (ProtoBuf.Meta.TypeModel model, ProtoBuf.ProtoReader reader, DataFormat format, Int32 tag, System.Type listType, System.Type itemType, System.Object& value) [0x00000] in <filename unknown>:0
  at ProtoBuf.Meta.TypeModel.TryDeserializeAuxiliaryType (ProtoBuf.ProtoReader reader, DataFormat format, Int32 tag, System.Type type, System.Object& value, Boolean skipOtherFields, Boolean asListItem, Boolean autoCreate, Boolean insideList) [0x00000] in <filename unknown>:0
  at ProtoBuf.Meta.TypeModel.DeserializeCore (ProtoBuf.ProtoReader reader, System.Type type, System.Object value, Boolean noAutoCreate) [0x00000] in <filename unknown>:0
  at ProtoBuf.Meta.TypeModel.Deserialize (System.IO.Stream source, System.Object value, System.Type type, ProtoBuf.SerializationContext context) [0x00000] in <filename unknown>:0
  at ProtoBuf.Meta.TypeModel.Deserialize (System.IO.Stream source, System.Object value, System.Type type) [0x00000] in <filename unknown>:0
  at Application.Mobile.TpDataAccess.Core.CoreProtobufDao`2[System.Guid,Application.Mobile.TpDataAccess.TimeEntryDao.Entities.TimeEntryDbo].EnsureCollection () [0x00000] in <filename unknown>:0
  at Application.Mobile.TpDataAccess.TimeEntryDao.TimeEntryProtobufDao.PrepareAccess () [0x00000] in <filename unknown>:0
  at Application.Mobile.TpBusinessComponents.TimeEntryService.TimeEntryService.PrepareAccess () [0x00000] in <filename unknown>:0
  at App.TP.Mobile.AppDelegate.FinishedLaunching (MonoTouch.UIKit.UIApplication app, MonoTouch.Foundation.NSDictionary options) [0x00031] in /Users/Developer/Dropbox/Application Mobile/Application Mobile iOS Project/Application_Mobile/AppDelegate.cs:34
  at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
  at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38
  at App.TP.Mobile.Application.Main (System.String[] args) [0x00000] in /Users/Developer/Dropbox/Application Mobile/Application Mobile iOS Project/Application_Mobile/Main.cs:17

问题是我们没有改变实体,也没有改变我们存储数据的方式。

// and serialize
string file = Path.Combine(StoragePath, dataFileName);
using (var stm = new FileStream(file, FileMode.Create))

    foreach (var item in list)
        Serializer.Serialize(stm, item);

无论我们读取哪个实体,它都会失败:

string file = Path.Combine(StoragePath, dataFileName);
if (!File.Exists(file))
    return;

using (var stm = new FileStream(file, FileMode.Open))

    var list = (TClass[]) Serializer.Deserialize(stm, null, typeof(TClass[]));
    if (list == null)
        return;

    // transform array into dictionary
    for (int i = 0; i < list.Length; i++)
    
        var entity = list[i];
        entities[primaryKeyFunction(entity)] = entity;
    

我们正在使用以下 Protobuf-NET 环境。

Protobuf-net r594 (unity dll) 预创建的序列化程序

保存数据不是问题,但加载失败。 非常感谢任何帮助。

谢谢 - 格哈德

【问题讨论】:

【参考方案1】:

一个不错的简单的。你已经序列化为Foo,反序列化为Foo[]。实际上,由于协议缓冲区是一种可附加格式,通过重复使用Serialize(到同一个文件),你实际上只写了一个Foo(但重复地覆盖了大部分成员)。

基本上,为了保留当前代码,您可以使用SerializeWithLengthPrefix 代替Serialize,即

foreach(var item in list)

    Serializer.SerializeWithLengthPrefix(stm, item,
        PrefixStyle.Base128, Serializer.ListItemTag);

但也可以简单地使用:

Serializer.Serialize(stm, list);

它们在语义上是相同的;在这两种情况下,你得到的是(以密集的二进制形式):

[field 1, string]
[payload length of item 0]
[payload of item 0]
[field 1, string]
[payload length of item 1]
[payload of item 1]
...
[field 1, string]
[payload length of item n]
[payload of item n]

正是当你告诉 TClass[] 时,Deserialize 正在寻找什么

【讨论】:

非常感谢。我会尽快检查的。 @BitKFu 请注意,这必然会更改线路上的数据,因此存在一些兼容性问题,但从根本上说,您当前的Serialize 循环在这种情况下看起来已经损坏。 @BitKFu 如果它在任何地方都有效,我会非常惊讶。形状被破坏了,绝对没有办法从循环中使用Serialize 恢复单个对象......你有一个具体的例子吗? @BitKFu 你能澄清一下:序列化循环中item 的类型是什么?什么是TClass?我可以看到它成功运行的唯一方法是item 实际上是List&lt;TClass&gt;,即一个列表项列表,但该循环会将其扁平化为一个项目列表 啊,马克,你是对的。我们针对 IOS 进行了更改,因为我们错过了预编译的序列化程序中正确的方法重载。原始:ProtoBuf.Serializer.Serialize(System.IO.Stream, T) 与预编译:ProtoBuf.Meta.TypeModel.Serialize(System.IO.Stream, object) 这使我们得出结论,我们必须迭代集合:)

以上是关于使用 MonoTouch 反序列化 Protobuf 文件的问题的主要内容,如果未能解决你的问题,请参考以下文章

Unity安装配置Python使用protobuf转换Excel表格数据并在unit中使用

了解gRPC一篇就够了

使用 gson 反序列化反序列化对象的内部对象

使用 Jackson 自定义反序列化:扩展默认反序列化器

使用C# json 二维数组 反序列化

Java反序列化漏洞(ysoserial工具使用shiro反序列化利用)