【问题标题】:C# Meta-information for object serialisation用于对象序列化的 C# 元信息
【发布时间】:2011-03-30 23:20:11
【问题描述】:

假设我有一个名为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...
   }
}

然后我基本上序列化一个对象,其中有一个序列化的流,它可能被压缩和/或加密,但我们现在不知道也不关心,因为元信息存储在SerialisationContainer .

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

另一个问题是string SerialisedData 的长度。在我给出的示例中,我们只有大约 5Gb 的 BinaryData,但是当它开始变大时呢?我知道string 在 64 位操作系统上的上限约为 2GB,而对于 32 位操作系统来说则要小得多。流有这样的限制吗?由于流是以字节块形式写入的,因此它们不会写入是有道理的。

【问题讨论】:

    标签: c# serialization encryption compression deserialization


    【解决方案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);
        }
    }
    

    【讨论】:

      【解决方案2】:

      有一次我就处于这种情况。我为手动写出的文件创建了一个标题,然后是压缩和/或加密(或可能是纯文本)流。当我打开文件时,我首先读取标题,然后根据该信息将输入流的位置设置为数据的开头,然后从中创建解压缩和/或解密流。它就像一个魅力,是小菜一碟,还有其他一些陈词滥调。

      我的标题是纯文本并包含:

      1. 在设计过程早期随机选择的一个短字符串,用于识别文件格式是否正确。

      2. 一个文件版本号,这样我们以后可以更改格式并仍然读取旧文件。

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

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

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

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

      然后是数据流。

      【讨论】:

      • 谢谢 :) 我没想过要写一个标题字节 - 但现在我想起来很明显。他们说事后诸葛亮总是 20/20。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-12
      • 2019-12-16
      • 1970-01-01
      • 2011-08-19
      • 1970-01-01
      • 2019-11-21
      相关资源
      最近更新 更多