C # мета-информация для сериализации объектов - PullRequest
0 голосов
/ 31 марта 2011

Скажем, у меня есть объект с именем 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. В примере, который я привел, у нас есть только около 5 ГБ BinaryData, но как насчет того, когда он начинает увеличиваться? Я знаю, что верхняя граница для string в 64-битной ОС составляет около 2 ГБ и значительно меньше для 32-битной ОС. Есть ли у потоков такое ограничение? Поскольку потоки записываются в байтовых блоках, имеет смысл, что они не будут.

Ответы [ 2 ]

1 голос
/ 31 марта 2011

Я был в той же ситуации один раз.Я создал заголовок для файла, который я записал вручную, за которым следуют сжатые и / или зашифрованные (или, возможно, простой текст) потоки.Когда я открыл файл, я сначала прочитал в заголовке, а затем на основе этой информации установил местоположение входного потока в начало данных, а затем создал распаковывающие и / или дешифрующие потоки из этого.Он работал как шарм, был просто пирогом и несколькими другими клише.

Мой заголовок был простым текстом и содержал:

  1. Короткая строка, которая была выбранана ранней стадии процесса проектирования, чтобы определить, что файл имеет правильный формат.

  2. Номер версии файла, чтобы мы могли изменить формат в будущем и при этом прочитать старые файлы.

  3. Различная сводная информация о бизнесе в виде простого текста, которая будет отображаться в списке, чтобы пользователь знал, какой файл открыть, даже если имя файла было изменено.Очевидно, это не были конфиденциальные данные.

  4. Индикатор, который указывал, был ли файл зашифрован, сжат или тем и другим.Кроме того, он может быть зашифрован целиком или построчно для поддержки добавления зашифрованных данных на лету.Параметр в виде простого текста использовался для целей разработки и периодической операции обработки данных, но из-за этого дизайна его можно было автоматически считывать или записывать как любой другой файл.

  5. Если файл был зашифрован с помощью AES, то затем был сохранен ключ шифрования, который сам был зашифрован с помощью RSA и сериализован с помощью base-64.

  6. Символ ASCII 0x02 START OF TEXT,это было чисто для развлечения.(Хотя, если бы его там не было, чтение файла было бы неудачным.)

Затем пришел поток данных.

1 голос
/ 31 марта 2011

Прежде всего, ленивое решение: вам не нужно сериализовать напрямую в файл. Вы можете сериализовать в память, а затем записать файл с форматом 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);
    }
}
...