【问题标题】:ArgumentException {token} while deserializing using protobuf.net使用 protobuf.net 反序列化时出现 ArgumentException {token}
【发布时间】:2014-01-15 19:23:59
【问题描述】:

我使用 protobuf-net 进行序列化和反序列化。

我遵循的方法是创建一个 RuntimeTypeModel 并添加所有需要序列化或支持序列化的类型。添加所有类型后,我使用 TypeModel.Compile() 以获得性能优势。

当一个类实例被序列化并作为 blob 保存到数据库中时,我能够在从数据库中获取时反序列化它。但是,当我更改机器并尝试反序列化时,我在

处收到类型为 token 的 ArgumentException
   at ProtoBuf.ProtoReader.EndSubItem(SubItemToken token, ProtoReader reader) in c:\Dev\protobuf-net\protobuf-net\ProtoReader.cs:line 584
   at ProtoBuf.ProtoReader.ReadTypedObject(Object value, Int32 key, ProtoReader reader, Type type) in c:\Dev\protobuf-net\protobuf-net\ProtoReader.cs:line 567
   at ProtoBuf.ProtoReader.ReadObject(Object value, Int32 key, ProtoReader reader) in c:\Dev\protobuf-net\protobuf-net\ProtoReader.cs:line 543
   at proto_157(Object , ProtoReader )
   at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read(Object value, ProtoReader source) in c:\Dev\protobuf-net\protobuf-net\Serializers\CompiledSerializer.cs:line 57
   at ProtoBuf.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source) in c:\Dev\protobuf-net\protobuf-net\Meta\RuntimeTypeModel.cs:line 783
   at ProtoBuf.ProtoReader.ReadTypedObject(Object value, Int32 key, ProtoReader reader, Type type) in c:\Dev\protobuf-net\protobuf-net\ProtoReader.cs:line 556
   at ProtoBuf.ProtoReader.ReadObject(Object value, Int32 key, ProtoReader reader) in c:\Dev\protobuf-net\protobuf-net\ProtoReader.cs:line 543
   at proto_190(Object , ProtoReader )
   at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read(Object value, ProtoReader source) in c:\Dev\protobuf-net\protobuf-net\Serializers\CompiledSerializer.cs:line 57
   at ProtoBuf.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source) in c:\Dev\protobuf-net\protobuf-net\Meta\RuntimeTypeModel.cs:line 783
   at ProtoBuf.Meta.TypeModel.DeserializeCore(ProtoReader reader, Type type, Object value, Boolean noAutoCreate) in c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:line 683
   at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type, SerializationContext context) in c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:line 582
   at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type) in c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:line 561

代码库是相同的,但在不同的机器上运行我遇到了这个问题。

参与序列化的类非常简单

public class NetworkData
{

}

/// <summary>
/// Class for representing dashboard URL item
/// </summary>
public class DashboardURLItem : NetworkData
{
    /// <summary>
    /// Gets and Sets the URL
    /// </summary>
    public string URL { get; set; }

    /// <summary>
    /// Gets and Sets the Header for the URL Item
    /// </summary>
    public string Header { get; set; }
}

对于序列化,我使用以下代码

    public byte[] SerializeData(T piclObjectData)
    {
        try
        {
            using (MemoryStream loclStream = new MemoryStream())
            {
                NWTypeModel.Serialize(loclStream, piclObjectData);
                byte[] resultbuffer = new byte[loclStream.Length + 4]; // Those many bytes were generated in serialization + Message Length
                Array.Copy(BitConverter.GetBytes(resultbuffer.Length), resultbuffer, 4);
                Array.Copy(loclStream.GetBuffer(), 0, resultbuffer, 4, loclStream.Position);
                return resultbuffer;
            }
        }
        catch (Exception E)
        {
            Logger.WriteLog(ToString() + ": Exception while serializing - " + E.Message);
            throw;
        }
    }

反序列化代码

    public T DeSerializeData(byte[] piarBuffer)
    {
        try
        {
            using (MemoryStream loclStream = new MemoryStream())
            {
                loclStream.Seek(0, SeekOrigin.Begin);
                loclStream.Write(piarBuffer, 4, piarBuffer.Length - 4); // 4 Bytes for Message length
                loclStream.Seek(0, SeekOrigin.Begin);
                return (T)NWTypeModel.Deserialize(loclStream, null, f_GenericType);
            }
        }
        catch (Exception E)
        {
            Logger.WriteLog(ToString() + ": Exception while de-serializing - " + E.Message);
        }
        return null;
    }

序列化和反序列化在同一台机器上为数据库工作正常,但是当从另一台机器上的数据库中获取值时,它无法反序列化。

如果我做错了什么或需要处理更多事情,请多多指教。

谢谢

【问题讨论】:

    标签: c# .net serialization protobuf-net argumentexception


    【解决方案1】:

    终于在花了几个小时调试 protobuf-net 源代码后,我发现了问题所在。 Protobuf-net 内部使用一个列表,并且没有对将添加的类型进行排序。因此,当我从机器 A 启动 exe 时,与机器 B 上的顺序相比,使用反射的类型以不同的顺序添加。不知道当代码库相同时反射是否应该发出相同的类型顺序。但是,我添加了一个 SortedList 来将类型存储在程序集中,然后添加到 RuntimeTypeModel。

    现在,由于机器 A 和机器 B 具有相同的代码来对类型进行排序,并且由于代码相同,因此不会有额外的类型,所以我最终在两边都对类型进行了正确的排序,并得到了正确的反序列化目的。机器A序列化一个对象并存储在Table的blob字段中,机器B从表中读取,反序列化然后消费该对象。

    排序很重要,因为我将序列化数据保存在数据库中,然后进行反序列化。但是,如果将来添加更多类型,则顺序将再次更改。我正在尝试一个万无一失的解决方案,以便可以在不破坏现有类型的序列化或反序列化的情况下添加未来的类型。

    应该有一种方法可以保留类型的顺序,如果检测到任何新类型,则应该在最后附加,这样现有类型的 MetaTypes 就不会受到干扰。

    【讨论】:

    • 当您说“添加类型”时 - 您是在谈论公共基类的子类型吗?因为这是唯一类型的顺序很重要的场景——它实际上不是“顺序”——它是它们的键。你能说得更具体些吗?
    • 是的@MarcGravell 我说的是子类型。我的要求是我有一个公共基类 NetworkData,所有参与序列化的类都是通过它派生的。当我使用反射添加类型及其子类型时,子类型的顺序在不同的机器上发生了变化。
    • “字段编号”参数非常重要:这是 only 被序列化的东西,指示类型。因此,您的代码必须能够在每台机器上可靠地生成相同的字段编号。我强烈建议将该信息存储为元数据或配置数据。非常刻意,protobuf 在序列化数据中没有类型的概念。
    • 注意:字段编号不需要连续 - 1、2、14、23、42 都可以,只要每台机器都知道 23 是 SuperFoo,14 是 MegaFoo,2 是 MicroFoo
    • 是的..我正在考虑这些行来存储字段顺序的元数据。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-06
    • 1970-01-01
    • 2016-12-22
    • 1970-01-01
    • 2010-11-25
    • 2023-04-06
    • 1970-01-01
    相关资源
    最近更新 更多