c#序列化数据

时间:2009-07-17 08:43:39

标签: c# serialization data-structures

我一直在使用BinaryFormatter将数据序列化到磁盘,但它看起来不太可扩展。我已经创建了一个200Mb的数据文件但是无法读回(在解析完成之前遇到的End of Stream)。它试图大约30分钟反序列化然后放弃。这是一个相当不错的四核CPU盒,带有8Gb RAM。

我正在序列化一个相当大的复杂结构。

htCacheItems是CacheItems的Hashtable。每个CacheItem都有几个简单的成员(字符串+整数等),还包含一个Hashtable和一个链表的自定义实现。子哈希表指向CacheItemValue结构,该结构当前是包含键和值的简单DTO。链表项也同样简单。

失败的数据文件包含大约400,000个CacheItemValues。

较小的数据集运行良好(虽然需要的时间比我希望的反序列化和使用大量内存的时间长。)

    public virtual bool Save(String sBinaryFile)
    {
        bool bSuccess = false;
        FileStream fs = new FileStream(sBinaryFile, FileMode.Create);

        try
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(fs, htCacheItems);
            bSuccess = true;
        }
        catch (Exception e)
        {
            bSuccess = false;
        }
        finally
        {
            fs.Close();
        }
        return bSuccess;
    }

    public virtual bool Load(String sBinaryFile)
    {
        bool bSuccess = false;

        FileStream fs = null;
        GZipStream gzfs = null;

        try
        {
            fs = new FileStream(sBinaryFile, FileMode.OpenOrCreate);

            if (sBinaryFile.EndsWith("gz"))
            {
                gzfs = new GZipStream(fs, CompressionMode.Decompress);
            }

            //add the event handler
            ResolveEventHandler resolveEventHandler = new ResolveEventHandler(AssemblyResolveEventHandler);
            AppDomain.CurrentDomain.AssemblyResolve += resolveEventHandler;

            BinaryFormatter formatter = new BinaryFormatter();
            htCacheItems = (Hashtable)formatter.Deserialize(gzfs != null ? (Stream)gzfs : (Stream)fs);

            //remove the event handler
            AppDomain.CurrentDomain.AssemblyResolve -= resolveEventHandler;

            bSuccess = true;
        }
        catch (Exception e)
        {
            Logger.Write(new ExceptionLogEntry("Failed to populate cache from file " + sBinaryFile + ". Message is " + e.Message));
            bSuccess = false;
        }
        finally
        {
            if (fs != null)
            {
                fs.Close();
            }
            if (gzfs != null)
            {
                gzfs.Close();
            }
        }
        return bSuccess;
    }

resolveEventHandler只是一个解决方法,因为我在一个应用程序中序列化数据并将其加载到另一个应用程序中(http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/e5f0c371-b900-41d8-9a5b-1052739f2521

问题是,我该如何改进?数据序列化总是效率低下,我最好编写自己的例程吗?

3 个答案:

答案 0 :(得分:2)

序列化很棘手,特别是当你想在版本控制方面有一定程度的灵活性时。

通常在可序列化的可移植性和灵活性之间进行权衡。例如,您可能希望使用Protocol Buffers(免责声明:我编写one of the C# ports)作为一个非常有效的解决方案,具有良好的可移植性和版本控制 - 但是您需要将您的自然数据结构转换为Protocol Buffers支持的东西。

话虽如此,我很惊讶二进制序列化在这里失败 - 至少以这种特殊方式。你能用一个非常非常简单的序列化代码的大文件来使它失败吗? (没有分辨率处理程序,没有压缩等。)

答案 1 :(得分:2)

我个人会尽量避免组装决心的需要;它有一定的气味。如果您必须使用BinaryFormatter,那么我只需将DTO放入可在两个应用程序中使用的单独库(dll)中。

如果您不想共享该dll,那么IMO您不应该使用BinaryFormatter - 您应该使用基于合同的序列化程序,例如XmlSerializer或{{1} },或者一个“协议缓冲区”实现(并重复Jon的免责声明:我写了one of the others)。

200MB看起来确实很大,但我不会指望它会失败。这里可能的一个原因是它为引用做的对象跟踪;但即便如此,这让我感到惊讶。

我希望看到一个简化的对象模型,看看它是否适合上述任何一种。


以下示例尝试使用protobuf-net从描述中镜像您的设置。奇怪的是,使用链接列表which I'll investigate似乎有一个小故障;但其余的似乎有效:

DataContractSerializer

答案 2 :(得分:1)

有助于级联序列化的东西。

调用mainHashtable.serialize(),例如返回XML字符串。此方法调用everyItemInYourHashtable.serialize(),依此类推。

您在每个类中使用静态方法执行相同的操作,称为“unserialize(String xml)”,它会反序列化您的对象并返回一个对象或一个对象列表。 你明白了吗?

当然,您需要在每个要序列化的类中实现此方法。

看一下ISerializable interface,它代表了我所描述的内容。 IMO,这个界面看起来太“微软”(没有使用DOM等),所以我创建了我的,但原理是一样的:级联。