反序列化二进制文件的一部分

6

能否对二进制文件的部分进行反序列化?

基本上,我有一个类似下面的对象,我将其序列化为二进制文件。

public class MyObject
{
    public string Name { get; set; }

    public int Value { get; set; }

    public IList<MyOtherObject> { get; set; } // lots of data in here (order of kB-MB)
}

我希望能够仅反序列化NameValue,以填充ListView以进行文件选择,并在需要时反序列化文件的其余部分(即用户从ListView中选择该文件)。
如常,非常感谢您的帮助。如果建议使用任何第三方库,则需要能够在商业环境中自由使用。

1
可能值得一提的是,您首先如何对文件进行序列化。 - musefan
只是想确认一下,列表视图项代表二进制文件的部分,您只想加载用户选择的那些部分? - Derek
@Derek ListView 显示摘要数据,就像资源管理器一样(即名称、文件大小、创建日期,但也包括一些特定于我的程序的自定义数据),然后理想情况是在需要时加载其余数据。 - dav_i
从技术上讲,所有序列化器都是“二进制”的(或多或少使用Encoding)。我想真正的问题是:你是否被绑定到BinaryFormatter - Marc Gravell
@MarcGravell 感谢您的建议(除了一些遗留数据(但我可以单独处理)),目前我正在研究您推荐的“protobuf-net”。谢谢。 - dav_i
显示剩余6条评论
2个回答

6

protobuf-net能够做到这一点,因为它不与特定的类型绑定;例如:

using ProtoBuf;
using System.Collections.Generic;
using System.IO;

[ProtoContract]
public class MyOtherObject { }
[ProtoContract]
public class MyObject
{
    [ProtoMember(1)]
    public string Name { get; set; }
    [ProtoMember(2)]
    public int Value { get; set; }
    [ProtoMember(3)]
    public IList<MyOtherObject> Items { get; set; }
}

[ProtoContract]
public class MyObjectLite
{
    [ProtoMember(1)]
    public string Name { get; set; }
    [ProtoMember(2)]
    public int Value { get; set; }
}

static class Program
{
    static void Main()
    {
        var obj = new MyObject
        {
            Name = "abc",
            Value = 123,
            Items = new List<MyOtherObject>
            {
                new MyOtherObject(),
                new MyOtherObject(),
                new MyOtherObject(),
                new MyOtherObject(),
            }
        };
        using (var file = File.Create("foo.bin"))
        {
            Serializer.Serialize(file, obj);
        }
        MyObjectLite lite;
        using (var file = File.OpenRead("foo.bin"))
        {
            lite= Serializer.Deserialize<MyObjectLite>(file);
        }
    }
}

但是,如果你不想要两种不同的类型,或者不想添加属性 - 这也是可以做到的:

using ProtoBuf.Meta;
using System.Collections.Generic;
using System.IO;

public class MyOtherObject { }
public class MyObject
{
    public string Name { get; set; }
    public int Value { get; set; }
    public IList<MyOtherObject> Items { get; set; }
}
static class Program
{
    static readonly RuntimeTypeModel fatModel, liteModel;
    static Program()
    {
        // configure models
        fatModel = TypeModel.Create();
        fatModel.Add(typeof(MyOtherObject), false);
        fatModel.Add(typeof(MyObject), false).Add("Name", "Value", "Items");
        liteModel = TypeModel.Create();
        liteModel.Add(typeof(MyOtherObject), false);
        liteModel.Add(typeof(MyObject), false).Add("Name", "Value");
    }
    static void Main()
    {
        var obj = new MyObject
        {
            Name = "abc",
            Value = 123,
            Items = new List<MyOtherObject>
            {
                new MyOtherObject(),
                new MyOtherObject(),
                new MyOtherObject(),
                new MyOtherObject(),
            }
        };
        using (var file = File.Create("foo.bin"))
        {
            fatModel.Serialize(file, obj);
        }
        MyObject lite;
        using (var file = File.OpenRead("foo.bin"))
        {
            lite = (MyObject)liteModel.Deserialize(
                file, null, typeof(MyObject));
        }
    }
}

1
+1. 很棒的是,您可以从同一字节数组中获取两个不同的模型。但是,在需要时是否有一种将“剩余部分”反序列化为lite的方法?如果我正确理解了这个解决方案,那么当您需要完整对象时,它需要您放弃lite并使用fatModel反序列化一个全新的对象。 - Eren Ersönmez
那么在你的第一个示例中,只要属性名称、类型和 ProtoMember 值相同,它就不关心反序列化的对象是什么? - dav_i
在某些情况下,@Eren,是的(protobuf是可合并的),但在一般情况下,我认为那只是错误的方法。 - Marc Gravell
@dav_i 实际上,protobuf 从不使用名称 - 只有 (1、2、3) 是重要的。在第二个示例中,该语法只是一种简写方式,表示“将这些配置为1、2、3等”。 - Marc Gravell
@dav_i Add("Name", "Value2", "Items") 只是一种简写方式;还有很多其他方法可用于定义模式。 - Marc Gravell
显示剩余5条评论

0

NameValue放到一个超类中并分别序列化它们怎么样?

或者,您可以维护一个字典并将其序列化为一个文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接