C#对象序列化的元信息

时间:2011-03-30 23:20:11

标签: c# serialization encryption compression deserialization

假设我有一个名为data的对象,其中包含各种信息。让我们说一下,data图中确实有很多东西。

如果我使用BinaryFormatter对其进行序列化,那么我会得到一个文件,例如5Mb。 如果我将序列化流封装在GZipStream中,那么我会得到一个更小的文件,比如1Mb。

如果需要,我可以在压缩流的同时加密流,或者在不压缩流的情况下加密流。

问题是:我需要知道在序列化过程中做了什么,以便在我对其进行反序列化时知道该怎么做。

一种技术是使用不同的文件扩展名。例如,未压缩的未加密文件可能具有.dat扩展名,压缩文件为.zdat,加密时为.cdat,压缩和加密时为.czdat。

这可行,但它引入了一个潜在的问题:如果用户更改扩展等,该怎么办?这也意味着如果我想在Windows中关联文件,则有4个扩展而不是1个需要关联的扩展 - 将与现有协会发生冲突的风险翻两番。

如果我将数据对象包装在一个简单的类中:

[Serializable]
public class SerialisationContainer
{
   public string SerialisedData { get; private set; }

   public bool Compressed { get; private set; }
   public bool Encrypted { get; private set; }

   public SerialisationContainer()
   {
     // etc...
   }

   public object GetObject()
   {
     // etc...
   }
}

然后我基本上序列化了一个对象,该对象中有一个可以压缩和/或加密的序列化流,但我们现在还不知道或不关心,因为元信息存储在{{1 }}

你怎么看?我基本上只是好奇你对这种方法的看法,以及你在类似情况下做了什么。我认为上述方法是一种非常浪费的方式来做我想做的事情。我基本上需要将我的数据图序列化为内存流,将其转换为字符串,将字符串放入我的容器中,然后再次序列化。

另一个问题是SerialisationContainer的长度。在我给出的示例中,我们只有大约5Gb的BinaryData,但是什么时候它开始变大?我知道64位操作系统上string SerialisedData的上限约为2GB,而32位操作系统的上限则要小得多。溪流有这样的局限吗?由于流是以字节为单位写的,因此它们不会有意义。

2 个答案:

答案 0 :(得分:1)

首先,懒惰的解决方案:您不必直接序列化到文件。您可以序列化到内存,然后编写一个文件,其中包含1个字节的格式,后跟序列化数据。

其次,你可以更聪明一点:打开文件;写一个字节(格式);序列化为相同的字符串。要反序列化,请读取一个字节以确定格式,然后将流传递给反序列化器;它只会在那一个字节之后读取数据。

如果你有方法

void SerializeToStream(Stream stream, bool compress, bool encrypt);
void DeserializeFromStream(Stream stream, bool compressed, bool encrypted);

您的代码可能如下所示:

// Could also use a flags enum for these
const int EncryptBit = 1;
const int CompressBit = 2;

public void SaveToFile(string filename, bool compress, bool encrypt) {
    byte format = (byte)((compress ? CompressBit : 0) | (encrypt ? EncryptBit : 0));
    using (Stream stream = File.OpenWrite(filename)) {
        stream.WriteByte(format);
        SerializeToStream(stream, compress, encrypt);
    }
}

public void LoadFromFile(string filename) {
    using (Stream stream = File.OpenRead(filename)) {
        int format = stream.ReadByte();
        if (format < 0 || format >= 4) {
            throw new InvalidOperationException("Unknown file format");
        }

        bool compressed = format & CompressBit != 0;
        bool encrypted = format & EncryptBit != 0;
        DeserializeFromStream(stream, compressed, encrypted);
    }
}

答案 1 :(得分:1)

我曾经遇到过这种情况。我为我手动编写的文件创建了一个标题,然后是压缩和/或加密(或可能是纯文本)流。当我打开文件时,我首先读入标题,然后根据该信息设置输入流的位置到数据的开头,然后从中创建解压缩和/或未加密的流。它像一个魅力,一块蛋糕,以及其他几个陈词滥调。

我的标题是纯文本,包括:

  1. 在设计过程的早期随机选择的短字符串,用于将文件标识为正确的格式。

  2. 文件版本号,因此我们可以在将来更改格式,并仍然可以阅读旧文件。

  3. 以明文形式显示的各种特定于业务的摘要信息,这些信息将显示在列表中,这样即使文件名已更改,用户也会知道要打开哪个文件。这显然不是安全敏感数据。

  4. 指示文件是加密,压缩还是两者都有的指示符。此外,它可以作为一个整体或逐行加密,以支持动态附加加密数据。纯文本选项用于开发目的和偶尔的数据外科操作,但由于这种设计,它可以像任何其他文件一样自动读取或写入。

  5. 如果文件是用AES加密的,则接下来存储加密密钥,加密密钥本身用RSA加密,并用base-64序列化。

  6. ASCII 0x02 START OF TEXT字符,纯粹是为了好玩。 (虽然如果不存在,那么阅读文件就会失败。)

  7. 然后是数据流。