如何使用protobuf序列化/反序列化List <class object =“”>?

时间:2017-03-28 22:18:05

标签: c# serialization protocol-buffers protobuf-net

我想序列化List<ArchiveData>,但它几乎总是失败。 Protobuff抛出以下异常:

  

无效的线型;这通常意味着你覆盖了一个文件   没有截断或设定长度;看到   Using Protobuf-net, I suddenly got an exception about an unknown wire-type

我已阅读该帖但仍无法找到解决方案。我如何序列化和反序列化?

我的班级和结构:

[Serializable, ProtoContract(Name = @"Archive"), ProtoInclude(1, typeof(List<ArchiveData>))]
public partial class Archive : IExtensible
{
    [ProtoMember(1, IsRequired = true, OverwriteList = true, Name = @"data", DataFormat = DataFormat.Default)]
    public List<ArchiveData> data { get; set; }

    public Archive()
    {
        data = new List<ArchiveForm.ArchiveData>();
    }

    private IExtension extensionObject;
    IExtension IExtensible.GetExtensionObject(bool createIfMissing)
    {
        return Extensible.GetExtensionObject(ref extensionObject, createIfMissing);
    }
}

public struct ArchiveData
{
    [ProtoMember(1, IsRequired = false, OverwriteList = true, Name = @"sourcefolder", DataFormat = DataFormat.Default)]
    [global::System.ComponentModel.DefaultValue("")]
    public string sourcefolder { get; set; }

    [ProtoMember(2, IsRequired = false, OverwriteList = true, Name = @"destinationfolder", DataFormat = DataFormat.Default)]
    [global::System.ComponentModel.DefaultValue("")]
    public string destinationfolder { get; set; }

    [ProtoMember(3, IsRequired = false, OverwriteList = true, Name = @"period", DataFormat = DataFormat.FixedSize)]
    [global::System.ComponentModel.DefaultValue((int)0)]
    public int period { get; set; }

    public ArchiveData(string sfolder = "", string dfolder = "", int priod = 0)
    {
        sourcefolder = sfolder;
        destinationfolder = dfolder;
        period = priod;
    }
}

我使用以下方法对其进行序列化:

public static void Refresh(ref Archive arc)
{
    if (File.Exists(probuffile))
    {
        using (var fs = File.OpenRead(probuffile))
        {
            arc = Serializer.Deserialize<Archive>(fs);
        }
    }
}

我用以下方法反序列化:

public static void Update(Archive arc)
{
    using (var fs = File.Create(probuffile))
    {
        Serializer.Serialize<Archive>(fs, arc);
        fs.SetLength(fs.Position);
    }
}

我用它:

Archive archive = new Archive();
//Add some ArchiveData.
Refresh(ref archive);

------------------------------编辑---------------- --------------

此部分已添加以获取更多信息。当我像下面的代码一样使用SerializeWithLengthPrefix / DeserializeWithLengthPrefix函数时,它每次都适用于我使用Deserialize函数的第一个类。但是对于我使用Deserialize函数的第二个类,它返回null。

[Serializable, ProtoContract(Name = @"OptionData")]
public class OptionData : IExtensible
{
    [ProtoMember(1, IsRequired = false, OverwriteList = true, Name = @"StartWithWindows", DataFormat = DataFormat.Default)]
    [DefaultValue(false)]
    public bool StartWithWindows { get; set; }

    [ProtoMember(2, IsRequired = false, OverwriteList = true, Name = @"AutoBackup", DataFormat = DataFormat.Default)]
    [DefaultValue(false)]
    public bool AutoBackup { get; set; }

    [ProtoMember(3, IsRequired = false, OverwriteList = true, Name = @"Speed", DataFormat = DataFormat.FixedSize)]
    [DefaultValue((int)0)]
    public int Speed { get; set; }

    private IExtension extensionObject;
    IExtension IExtensible.GetExtensionObject(bool createIfMissing)
    {
        return Extensible.GetExtensionObject(ref extensionObject, createIfMissing);
    }
}

public static void Update(OptionData op)
{
    using (var fs = File.Create(probuffile))
    {
        Serializer.SerializeWithLengthPrefix(fs, op, PrefixStyle.Base128, 3);
    }
}

public static void Update(Archive arc)
{
    using (var fs = File.Create(probuffile))
    {
        Serializer.SerializeWithLengthPrefix<Archive>(fs, arc, PrefixStyle.Base128, 2);
    }
}

public static void Refresh(ref OptionData op)
{
    if (File.Exists(probuffile))
    {
        using (var fs = File.OpenRead(probuffile))
        {
            op = Serializer.DeserializeWithLengthPrefix<OptionData>(fs, PrefixStyle.Base128, 3);
        }
    }
}

public static void Refresh(ref Archive arc)
{
    if (File.Exists(probuffile))
    {
        using (var fs = File.OpenRead(probuffile))
        {
            arc = Serializer.DeserializeWithLengthPrefix<Archive>(fs, PrefixStyle.Base128, 2);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

我很乐意提供帮助,但我正在努力让它失败。你说&#34;但它几乎总是失败&#34;,但是下面的可运行的控制台exer工作正常并且写了大量不同大小的数据:

using ProtoBuf;
using System;
using System.Collections.Generic;
using System.IO;
static class P
{
    static void Main()
    {
        var random = new Random(12345);
        // start with 100
        var archive = CreateData(random, 100);
        Update(archive);
        Refresh(ref archive);

        // make it bigger
        archive = CreateData(random, 200);
        Update(archive);
        Refresh(ref archive);

        // make it smaller
        archive = CreateData(random, 50);
        Update(archive);
        Refresh(ref archive);

        // go wild
        for (int i = 0; i < 1000; i++)
        {
            archive = CreateData(random, random.Next(0, 500));
            Update(archive);
            Refresh(ref archive);
        }

    }
    static Archive CreateData(Random random, int dataCount)
    {
        var archive = new Archive();
        var data = archive.data;
        for (int i = 0; i < dataCount; i++)
        {
            data.Add(new ArchiveData
            {
                period = random.Next(10000),
                sourcefolder = CreateString(random, 50),
                destinationfolder = CreateString(random, 50)
            });
        }
        return archive;
    }

    private static unsafe string CreateString(Random random, int maxLength)
    {
        const string Alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
        int len = random.Next(maxLength + 1);
        char* chars = stackalloc char[len];
        for (int i = 0; i < len; i++)
        {
            chars[i] = Alphabet[random.Next(Alphabet.Length)];
        }
        return new string(chars, 0, len);
    }

    static string probuffile = "my.bin";
    public static void Refresh(ref Archive arc)
    {
        if (File.Exists(probuffile))
        {
            using (var fs = File.OpenRead(probuffile))
            {
                arc = Serializer.Deserialize<Archive>(fs);
                Console.WriteLine("Read: {0} items, {1} bytes", arc.data.Count, fs.Length);
            }
        }
    }

    public static void Update(Archive arc)
    {
        using (var fs = File.Create(probuffile))
        {
            Serializer.Serialize<Archive>(fs, arc);
            fs.SetLength(fs.Position);
            Console.WriteLine("Wrote: {0} items, {1} bytes", arc.data.Count, fs.Length);
        }
    }
}

[Serializable, ProtoContract(Name = @"Archive"), ProtoInclude(1, typeof(List<ArchiveData>))]
public partial class Archive : IExtensible
{
    [ProtoMember(1, IsRequired = true, OverwriteList = true, Name = @"data", DataFormat = DataFormat.Default)]
    public List<ArchiveData> data { get; set; }

    public Archive()
    {
        data = new List<ArchiveData>();
    }

    private IExtension extensionObject;
    IExtension IExtensible.GetExtensionObject(bool createIfMissing)
    {
        return Extensible.GetExtensionObject(ref extensionObject, createIfMissing);
    }
}
[ProtoContract]
public struct ArchiveData
{
    [ProtoMember(1, IsRequired = false, OverwriteList = true, Name = @"sourcefolder", DataFormat = DataFormat.Default)]
    [global::System.ComponentModel.DefaultValue("")]
    public string sourcefolder { get; set; }

    [ProtoMember(2, IsRequired = false, OverwriteList = true, Name = @"destinationfolder", DataFormat = DataFormat.Default)]
    [global::System.ComponentModel.DefaultValue("")]
    public string destinationfolder { get; set; }

    [ProtoMember(3, IsRequired = false, OverwriteList = true, Name = @"period", DataFormat = DataFormat.FixedSize)]
    [global::System.ComponentModel.DefaultValue((int)0)]
    public int period { get; set; }

    public ArchiveData(string sfolder = "", string dfolder = "", int priod = 0)
    {
        sourcefolder = sfolder;
        destinationfolder = dfolder;
        period = priod;
    }
}

我猜测你的真实代码与上面的例子之间的区别是:问题的原因也是如此。如果你能帮助找到不同的东西,我会非常乐意为你提供合理的帮助。

作为次要注意事项:在使用Create时,您实际上并不需要设置长度,因为这显然是&#34;丢弃任何现有数据&#34;。您需要担心长度的主要时间是人们使用OpenWrite来覆盖现有文件,然后编写 less 数据。但是{em>问题并没有明确设置Create之后的长度