среда, 16 июля 2014 г.

Про "сериализацию" и "архитектуру"

Пусть нам надо уметь сохранять/восстанавливать состояние объекта в поток.

Можно сделать так:

type
 TMyClass = class
  private
   f_Integer : Integer;
   f_Date : TDateTime;
   f_Cardinal : Cardinal;
  public
   procedure SaveTo(aStream: TStream);
   procedure LoadFrom(aStream: TStream);
 end;//TMyClass

procedure TMyClass.SaveTo(aStream: TStream);
begin
 aStream.Write(f_Integer, SizeOf(f_Integer);
 aStream.Write(f_Date, SizeOf(f_Date);
 aStream.Write(f_Cardinal, SizeOf(f_Cardinal);
end;

procedure TMyClass.LoadFrom(aStream: TStream);
begin
 aStream.Read(f_Integer, SizeOf(f_Integer);
 aStream.Read(f_Date, SizeOf(f_Date);
 aStream.Read(f_Cardinal, SizeOf(f_Cardinal);
end;

Но лучше так:

type
 TMyClass = class
  private
   f_Integer : Integer;
   f_Date : TDateTime;
   f_Cardinal : Cardinal;
  public
   procedure SaveTo(aStream: TStream);
   procedure LoadFrom(aStream: TStream);
 end;//TMyClass

procedure TMyClass.SaveTo(aStream: TStream);
begin
 aStream.WriteBuffer(f_Integer, SizeOf(f_Integer);
 aStream.WriteBuffer(f_Date, SizeOf(f_Date);
 aStream.WriteBuffer(f_Cardinal, SizeOf(f_Cardinal);
end;

procedure TMyClass.LoadFrom(aStream: TStream);
begin
 aStream.ReadBuffer(f_Integer, SizeOf(f_Integer);
 aStream.ReadBuffer(f_Date, SizeOf(f_Date);
 aStream.ReadBuffer(f_Cardinal, SizeOf(f_Cardinal);
end;

А ещё лучше так:

type
 TStreamHelper = class
  private
   f_Stream : TStream;
  public
   constructor Create(aStream: TStream);
 end;//TStreamHelper

 TReader = class(TStreamHelper)
  public
   function ReadInteger;
   function ReadDate;
   function ReadCardinal;
 end;//TReader

 TWriter = class(TStreamHelper)
  public
   procedure WriteInteger(aValue: Integer);
   procedure WriteDate(aValue: TDateTime);
   procedure WriteCardinal(aValue: Cardinal);
 end;//TWriter

 TMyClass = class
  private
   f_Integer : Integer;
   f_Date : TDateTime;
   f_Cardinal : Cardinal;
  public
   procedure SaveTo(aStream: TWriter);
   procedure LoadFrom(aStream: TReader);
 end;//TMyClass

constructor TStreamHelper.Create(aStream: TStream);
begin
 inherited Create;
 f_Stream := aStream;
end;

function TReader.ReadInteger;
begin
 f_Stream.ReadBuffer(Result, SizeOf(Result));
end;

function TReader.ReadDate;
begin
 f_Stream.ReadBuffer(Result, SizeOf(Result));
end;

function TReader.ReadCardinal;
begin
 f_Stream.ReadBuffer(Result, SizeOf(Result));
end;

procedure TWriter.WriteInteger(aValue: Integer);
begin
 f_Stream.WriteBuffer(aValue, SizeOf(aValue));
end;

procedure TWriter.WriteDate(aValue: TDateTime);
begin
 f_Stream.WriteBuffer(aValue, SizeOf(aValue));
end;

procedure TWriter.WriteCardinal(aValue: Cardinal);
begin
 f_Stream.WriteBuffer(aValue, SizeOf(aValue));
end;

procedure TMyClass.SaveTo(aStream: TWriter);
begin
 aStream.WriteInteger(f_Integer);
 aStream.WriteDate(f_Date);
 aStream.WriteCardinal(f_Cardinal);
end;

procedure TMyClass.LoadFrom(aStream: TReader);
begin
 f_Integer := aStream.ReadInteger;
 f_Date := aStream.ReadDate;
 f_Cardinal := aStream.ReadCardinal;
end;

Понятно почему?

Потому что в TReader и TWriter можно убрать "настоящую сериализацию".

Например сделать (грубо) так:

type
 TStreamHelper = class
  private
   f_Stream : TStream;
  public
   constructor Create(aStream: TStream);
 end;//TStreamHelper

 TSignature = (sigInteger, sigDate, sigCardinal);

 TReader = class(TStreamHelper)
  private
   procedure CheckSignature(aSig: TSignature);
  public
   function ReadInteger;
   function ReadDate;
   function ReadCardinal;
   constructor Create(aStream: TStream);
 end;//TReader

 TWriter = class(TStreamHelper)
  private
   procedure WriteSignature(aSig: TSignature);
  public
   procedure WriteInteger(aValue: Integer);
   procedure WriteDate(aValue: TDateTime);
   procedure WriteCardinal(aValue: Cardinal);
   constructor Create(aStream: TStream);
 end;//TWriter

 TMyClass = class
  private
   f_Integer : Integer;
   f_Date : TDateTime;
   f_Cardinal : Cardinal;
  public
   procedure SaveTo(aStream: TWriter);
   procedure LoadFrom(aStream: TReader);
 end;//TMyClass

constructor TStreamHelper.Create(aStream: TStream);
begin
 inherited Create;
 f_Stream := aStream;
end;

procedure TReader.CheckSignature(aSig: TSignature);
var
 l_Sig : TSignature;
begin
 f_Stream.ReadBuffer(l_Sig, SizeOf(l_Sig));
 Assert(l_Sig = aSig);
end;

const
 cVersion = 1;

constructor TReader.Create(aStream: TStream);
begin
 inherited;
 Assert(ReadCardinal = cVersion);
end;

function TReader.ReadInteger;
begin
 CheckSignature(sigInteger);
 f_Stream.ReadBuffer(Result, SizeOf(Result));
end;

function TReader.ReadDate;
begin
 CheckSignature(sigDate);
 f_Stream.ReadBuffer(Result, SizeOf(Result));
end;

function TReader.ReadCardinal;
begin
 CheckSignature(sigCardinal);
 f_Stream.ReadBuffer(Result, SizeOf(Result));
end;

constructor TWriter.Create(aStream: TStream);
begin
 inherited;
 WriteCardinal(cVersion);
end;

procedure TWriter.WriteSignature(aSig: TSignature);
begin
 f_Stream.WriteBuffer(aSig, SizeOf(aSig));
end;

procedure TWriter.WriteInteger(aValue: Integer);
begin
 WriteSignature(sigInteger);
 f_Stream.WriteBuffer(aValue, SizeOf(aValue));
end;

procedure TWriter.WriteDate(aValue: TDateTime);
begin
 WriteSignature(sigDate);
 f_Stream.WriteBuffer(aValue, SizeOf(aValue));
end;

procedure TWriter.WriteCardinal(aValue: Cardinal);
begin
 WriteSignature(sigCardinal);
 f_Stream.WriteBuffer(aValue, SizeOf(aValue));
end;

procedure TMyClass.SaveTo(aStream: TWriter);
begin
 aStream.WriteInteger(f_Integer);
 aStream.WriteDate(f_Date);
 aStream.WriteCardinal(f_Cardinal);
end;

procedure TMyClass.LoadFrom(aStream: TReader);
begin
 f_Integer := aStream.ReadInteger;
 f_Date := aStream.ReadDate;
 f_Cardinal := aStream.ReadCardinal;
end;

Почему ReadXXX это функция, а не процедура?

А потому, что можно сделать так:

type
 TStreamHelper = class
  private
   f_Stream : TStream;
  public
   constructor Create(aStream: TStream);
 end;//TStreamHelper

 TSignature = (sigInteger, sigDate, sigCardinal);

 TReader = class(TStreamHelper)
  private
   procedure CheckSignature(aSig: TSignature);
  public
   function ReadInteger;
   function ReadDate;
   function ReadCardinal;
 end;//TReader

 TWriter = class(TStreamHelper)
  private
   procedure WriteSignature(aSig: TSignature);
  public
   procedure WriteInteger(aValue: Integer);
   procedure WriteDate(aValue: TDateTime);
   procedure WriteCardinal(aValue: Cardinal);
 end;//TWriter

 TMyClass = class
  private
   f_Integer : Integer;
   f_Date : TDateTime;
   f_Cardinal : Cardinal;
  public
   property IntegerProp: Integer read f_Integer write f_Integer;
   property DateProp: Integer read f_Date write f_Date;
   property CardinalProp: Integer read f_Cardinal write f_Cardinal;
  public
   procedure SaveTo(aStream: TWriter);
   procedure LoadFrom(aStream: TReader);
 end;//TMyClass

constructor TStreamHelper.Create(aStream: TStream);
begin
 inherited Create;
 f_Stream := aStream;
end;

procedure TReader.CheckSignature(aSig: TSignature);
var
 l_Sig : TSignature;
begin
 f_Stream.ReadBuffer(l_Sig, SizeOf(l_Sig));
 Assert(l_Sig = aSig);
end;

function TReader.ReadInteger;
begin
 CheckSignature(sigInteger);
 f_Stream.ReadBuffer(Result, SizeOf(Result));
end;

function TReader.ReadDate;
begin
 CheckSignature(sigDate);
 f_Stream.ReadBuffer(Result, SizeOf(Result));
end;

function TReader.ReadCardinal;
begin
 CheckSignature(sigCardinal);
 f_Stream.ReadBuffer(Result, SizeOf(Result));
end;

procedure TWriter.WriteSignature(aSig: TSignature);
begin
 f_Stream.WriteBuffer(aSig, SizeOf(aSig));
end;

procedure TWriter.WriteInteger(aValue: Integer);
begin
 WriteSignature(sigInteger);
 f_Stream.WriteBuffer(aValue, SizeOf(aValue));
end;

procedure TWriter.WriteDate(aValue: TDateTime);
begin
 WriteSignature(sigDate);
 f_Stream.WriteBuffer(aValue, SizeOf(aValue));
end;

procedure TWriter.WriteCardinal(aValue: Cardinal);
begin
 WriteSignature(sigCardinal);
 f_Stream.WriteBuffer(aValue, SizeOf(aValue));
end;

procedure TMyClass.SaveTo(aStream: TWriter);
begin
 aStream.WriteInteger(f_Integer);
 aStream.WriteDate(f_Date);
 aStream.WriteCardinal(f_Cardinal);
end;

procedure TMyClass.LoadFrom(aStream: TReader);
begin
 IntegerProp := aStream.ReadInteger;
 DateProp := aStream.ReadDate;
 CardinalProp := aStream.ReadCardinal;
end;

Мысль понятна?

Что-то напоминает?

А что ещё можно сделать?

А можно ВООБЩЕ - НЕ СВЯЗЫВАТЬСЯ с "бинарной сериализацией".

Как?

Позже попробую написать.

А пока почитайте вот что:

http://programmingmindstream.blogspot.ru/2014/04/sax.html
http://programmingmindstream.blogspot.ru/2014/04/blog-post_24.html
http://programmingmindstream.blogspot.ru/2014/04/blog-post_25.html
http://programmingmindstream.blogspot.ru/2014/04/blog-post_104.html

+Igor Belyh Это кстати для вас :-) В продолжение к тому, над чем мы трудимся.

И ещё ссылка - https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/Reference/Reference.html

И ещё:
https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/Blogger/RealWork/EVD/evdReader.pas
https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/Blogger/RealWork/EVD/evdWriter.pas
https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/Blogger/RealWork/K2/k2DocumentBuffer.pas
https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/Blogger/RealWork/K2/k2DocumentGenerator.pas

Возвращаясь к "бинарной сериализации" - если всё таки "с ней решили связываться".

Понятно, что "следующий шаг" вот такой?

function TReader.ReadTYPE(const aName: String): TYPE;
begin
 CheckSignature(TYPE);
 Assert(aName = ReadString);
 f_Stream.ReadBuffer(Result, SizeOf(Result);
end;

procedure TWriter.WriteTYPE(const aName: String; aValue: TYPE);
begin
 WriteSignature(TYPE);
 WriteString(aName);
 f_Stream.WriteBuffer(aValue, SizeOf(aValue);
end;

Ну и "вдогонку":
http://programmingmindstream.blogspot.ru/2014/06/blog-post_18.html
http://programmingmindstream.blogspot.ru/2014/06/blog-post_5984.html

Но ПОВТОРЮ, что ОСНОВНАЯ мысль поста в том, что "с бинарной сериализацией" - лучше не связываться.

Лучше оперировать понятием "схемы данных".

И чтение/запись проводить через СХЕМУ, а не "напрямую" из классов.

Ну и ещё:

https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/Blogger/RealWork/EVD/evdXMLReader.pas
https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/Blogger/RealWork/EVD/evdCustomXMLReader.pas
https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/Blogger/RealWork/EVD/evdXMLWriter.pas
https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/Blogger/RealWork/L3/l3XMLWriter.pas

Почему "не связываться с бинарной сериализацией"?

А потому, что "персистентные хранилища" - они ОЧЕНЬ ПЛОХО к ней относятся. Даже "запись номера версии" - НЕ ПОМОГАЕТ.

У меня когда-то была "бинарная сериализация". В "середине девяностых".

Потом я её заменил на "схему". С поддержкой "бинарной сериализации".

Потом я эту поддержку ГРОХНУЛ. Не так давно. Думая - "ну наверное таких документов уже и в природе не осталось"...

И что вы думаете?

Мне начали "всплывать документы" из "середины 90-х"... И Пользователи начали жаловаться....

Такая вот "долгая память" у "бинарной сериализации"...

37 комментариев:

  1. Зачем для сериализации нужна какая-то непонятная "схема данных"?
    Для решения задачи сохранения состояния объекта эта "схема данных" есть "из коробки" - структура объекта, доступная через RTTI. Ну плюс один-два виртуальных метода для отражения специфики, не укладывающейся в общую схему.
    Или Вы не о сериализации, а о маршаллинге говорите?
    Тогда понятно: если "на том конце" может быть не Delphi, то какая-то схема, описывающая интерфейс взаимодействия (сигнатуры методов, например, или передаваемые структуры) может пригодится.

    ОтветитьУдалить
    Ответы
    1. Зачем? Этот вопрос надо задать "миллионам людей", которые почему-то "предпочитают бинарную сериализацию". Почему? Вот я тоже хотел бы их спросить. Я на такой код насмотрелся - "выше крыши".

      Потому и написал этот пост.

      Насчёт "из коробки" - конечно МОЖНО. Но тут есть "тонкости".

      Во-первых "из коробки" это и есть уже "некая схема данных".
      Во-вторых - вы про published или про "новый RTTI"?

      Если про Published - Там тоже есть тонкости - например TPersistent.
      Если про "новый RTTI", то тем более - потому, что "сериализовать" обычно надо не всё, а лишь что-то. Т.е. это либо "атрибуты" типа [Serealizeable], либо - "мапа". Что тоже является "схемой данных".

      Удалить
    2. «Зачем? Этот вопрос надо задать "миллионам людей", которые почему-то "предпочитают бинарную сериализацию".»
      -- Хорошо, что Вы взяли "миллионы людей" в кавычки :-)
      Я из этих "миллионов" знаю только нас (ряд поддерживаемых проектов) да Вас, с Вашим постом про "бинарную сериализацию". IMHO это архаичная техника, которая малополезна в нынешних условиях...

      «Во-первых "из коробки" это и есть уже "некая схема данных"»
      -- Да, я об этом и сказал. Структура объекта может быть получена средствами RTTI.

      «Во-вторых - вы про published или про "новый RTTI"?»
      -- Вообще-то я написал просто RTTI. Информация о типах во время выполнения позволяет получить почти всё необходимое для сериализации. В частности, поля отмеченные как published.

      «Там тоже есть тонкости - например TPersistent.»
      -- Что за тонкости если не секрет? Уж не состоят ли они в том, что от TPersistent нужно наследовать, чтобы обеспечить сериализацию?
      Ну так:
      «..."сериализовать" обычно надо не всё, а лишь что-то. Т.е. это либо "атрибуты" типа [Serealizeable], либо - "мапа".»
      -- Именно так. Не уверен правда, что там нужна какая-то "мапа". Скорее даже - наоборот.
      Гораздо проще сериализовать не состояние объекта (класса T), а действительно только то, что требуется.
      Для этого "что требуется" легко определить соответствующий класс-потомок TPersistent (класс T', дружественный T). Его published-поля будут хранить то, что мы хотим сериализовать из объекта класса T.
      У T' будет конструктор, который будет принимать экземпляр T в качестве параметра (для последующей сериализации экземпляра T'), и метод, выполняющий построение экземпляра T (когда получив экземпляр T' в результате десериализации потребуется получить по нему экземпляр T).
      Схема действий при сериализации: для каждого объекта, подлежащего сериализации (T) получить его "образ" (T') и выполнить его сериализацию. При десериализации - выполнить загрузку "образов" (T') и выполнить построение требуемых объектов T.
      Роль "схемы данных" играют классы T', предназначенные исключительно для сериализации соответствующих им классов T. Роль "[Serializable]" - published-свойства T', специально предназначенные для сериализации соответствующих им свойств T.
      У этой схемы есть один очень существенный плюс.
      Поскольку T и T' - разные классы, они создаются независимо, T может измениться очень существенно, но всё равно, чаще всего нетрудно обеспечить корректное построение нового T по старому T'.
      Это означает, что мы получаем гораздо большую свободу по рефакторингу T, не сильно оглядываясь на то, что он мог быть сериализован и нужно обеспечивать "совместимость". Поскольку вместо T сериализовался T', изменив реализацию T, мы можем адаптировать реализацию T', чтобы она на основе старых сериализованных данных T' построила новый T после рефакторинга, выставив в умолчательные значения те поля, которые не сериализовались.
      Надеюсь, я понятно...

      Удалить
    3. Считайте это "гласом вопиющего"...

      Удалить
    4. Вы ПРАВИЛЬНО всё пишете... Но почему-то "на практике" - я вижу не совсем то...

      Удалить
    5. "Вообще-то я написал просто RTTI. Информация о типах во время выполнения позволяет получить почти всё необходимое для сериализации. В частности, поля отмеченные как published."

      "В *частности*, поля отмеченные как published"

      -- т.е. всё же про "новый RTTI"... Я понял...

      Это по-любому - Delphi XE*

      Удалить
    6. "IMHO это архаичная техника, которая малополезна в нынешних условиях"
      -- именно ТАК, об ЭТО пост собственно и БЫЛ.

      Удалить
    7. "его "образ""

      -- "зеркальный объект", что я и написал...

      Удалить
    8. И отображение объекта в "зеркальный объект" и наоборот - и ЕСТЬ "схема данных".

      О чём собственно пост.

      Удалить
    9. «"В *частности*, поля отмеченные как published"
      -- т.е. всё же про "новый RTTI"... Я понял...
      Это по-любому - Delphi XE*»

      -- Да при чём здесь "новый"? Published-свойства объектов, их типы, published-методы объектов можно было "по жизни" в Delphi обрабатывать.
      С тех пор, как модуль TypInfo появился.
      В том, что я описал ничего кроме этого не нужно.

      «"его "образ""
      -- "зеркальный объект", что я и написал...»

      -- Александр, я не знаю, что такое "зеркальный объект" :-)
      Этот термин мне неизвестен, и я не знаю, что он значит для Вас.
      То, что используется нами я обозначил.

      «И отображение объекта в "зеркальный объект" и наоборот - и ЕСТЬ "схема данных".
      О чём собственно пост.»

      -- Опять же, Александр... Для меня это никакая не "схема данных", поскольку схема обычно подразумевает структуру данных, обрабатываемую алгоритмами, которым эта структура известна. В данном случае структура конечно есть (RTTI), но она не имеет никакого отношения к соответствию свойств T и T'.
      Это соответствие описываются кодом, а не данными. И это - фича, поскольку обеспечивает максимальную гибкость при минимуме усилий. В том смысле наблюдается некая аналогия с Вашей "бинарной сериализацией", но без "порочных наклонностей" вроде последовательности сохранения (от которой, впрочем, легко уйти) и версионности на уровне кода.
      T' просто заполняет свои поля из экземпляра T в конструкторе, и создаёт и настраивает экземпляр T по запросу. В сущности, у его те же Load и Store, только переписанные на другом технологическом уровне.
      Версионность легко поддерживается многообразием классов T', соответствующих одному T.
      Кстати, у нас классы T' называют "фабриками [сериализации]", которые впрочем, имеют отдалённое отношение к паттерну Factory.

      Удалить
    10. Больше мнений хороших и разных :-)

      Удалить
    11. «"В *частности*, поля отмеченные как published"
      -- т.е. всё же про "новый RTTI"... Я понял...
      Это по-любому - Delphi XE*»
      -- Да при чём здесь "новый"? Published-свойства объектов, их типы, published-методы объектов можно было "по жизни" в Delphi обрабатывать.
      С тех пор, как модуль TypInfo появился.

      Про Published и TypInfo - я в курсе.

      Но словосочетание "в частности" - вы использовали преднамеренно или по ошибке?

      Удалить
    12. "обрабатываемую алгоритмами, которым эта структура известна"

      -- это как? :-) Расскажете?

      Удалить
    13. @NameRec

      -- вы кстати пробовали создавать/читать dfm'ы размером в десятки/сотни мегабайт?

      Удалить
    14. Налоговый кодекс или Гражданский кодекс - это документы порядка 12-ти мегабайт.
      Справочник Банковских Идентификационных Кодов - это документ порядка 30-ти мегабайт.
      Справочник ОКОФ или ОКОНХ - это документы порядка 30-ти мегабайт.

      Я их не пробовал в dfm сериализовать, но подозреваю, что выйдет - больше.

      Справочник БИК кстати в RTF - выливается. Но Word его читать отказывается (САМЫЙ современный - не проверял). Тупо - "память кончается". Libre или OpenOffice - читают. Но с некоторой потерей данных.

      Удалить
    15. Интересная кстати задачка - "вылить БИК в dfm-like формат", пусть и бинарный. Не думаю, что я это быстро сделаю. Но когда я это сделаю - я отпишусь о результатах.

      Удалить
    16. Этот комментарий был удален автором.

      Удалить
    17. «Но словосочетание "в частности" - вы использовали преднамеренно или по ошибке?»
      -- Вопрос не понят.
      1. RTTI позволяет не только обрабатывать published свойства.
      2. Для большинства задач сериализации от RTTI достаточно published-свойств.
      Поэтому, упомянув про RTTI в контексте сериализации я употребил "в частности".

      «"обрабатываемую алгоритмами, которым эта структура известна"
      -- это как? :-) Расскажете?»

      -- Ну например, о схеме данных я счёл бы уместным говорить в случае, если бы T' содержал где-нибудь в своём InitInstance построение словаря вида: {<имя свойства T'>: <имя свойства в T>}. О такой структуре можно было бы говорить как о какой-то "схеме данных", посредством которой можно было бы автоматизировать некоторые действия. Например:
      * Убедиться в наличии соответствующих свойств в T и T', а также соответствие их типов.
      * Выполнить при инициализации T' по T установку свойств T', а при создании экземпляра T в T' - обратную операцию.
      Но в данном случае этот подход мне не представляется уместным.

      «@NameRec
      -- вы кстати пробовали создавать/читать dfm'ы размером в десятки/сотни мегабайт?»

      -- Нет. И я не нахожу удачной идею использования сериализации "из коробки" для таких применений удачной.
      Самый большой сериализованный объект, с которым я оказался готов мириться - это отчёт размером 1,5 или 2 MB. Но отчёт действительно был очень большой.
      "Десятки-сотни мегабайт DFM" соответствуют десяткам тысяч объектов, попавших в сериализацию, что уже немного странно, принимая во внимание, что число разновидностей этих объектов исчезающе мало в сравнении с количеством этих объектов. В таких случаях обычно (исключения мне неизвестны) предпочтительнее использовать "табличный подход" к сохранению, заключающийся в том, что вначале сериализуется информация о структуре объекта [[<имя свойства 1>, <тип свойства 1>], [<имя свойства 2>, <тип свойства 2>], ...], а сами объекты сохраняются в виде списков значений [<зн. свойства 1>, <зн. свойства 2>], а не словарей вида {<ключ>: <значение>}.
      Естественным представлением такой совокупности будет JSON или его бинарные аналоги (BSON, Hessian...).
      Вообще же, хотелось бы понять, в каких задачах возникает потребность в таком количестве одновременно существующих объектов, которые и сериализовать нужно к тому же... Это что, граф связей объектов предметной области, с возможностью пользователю вносить изменения в его структуру и расположение элементов?
      Мне известна одна такая задача. Но там для сохранения состояния объектов была использована СУБД, кстати HyTech :-)

      Удалить
    18. "RTTI позволяет не только обрабатывать published свойства."

      - каким образом, если мы говорим о "свойствах класса".

      Удалить
    19. "Самый большой сериализованный объект, с которым я оказался готов мириться - это отчёт размером 1,5 или 2 MB. Но отчёт действительно был очень большой."

      -- вот вам и ответ.

      Удалить
    20. "Вообще же, хотелось бы понять, в каких задачах возникает потребность в таком количестве одновременно существующих объектов, которые и сериализовать нужно к тому же..."

      -- я же вам написал - я занимаюсь ОБРАБОТКОЙ документов. БОЛЬШИХ.

      Удалить
    21. "И я не нахожу удачной идею использования сериализации "из коробки" для таких применений удачной."

      -- вот вам и ответ - "зачем схема данных".

      Удалить
    22. "Естественным представлением такой совокупности будет JSON или его бинарные аналоги (BSON, Hessian...)."

      -- JSON - "не очень тянет"... Бинарные аналоги - не пробовал. Ибо уже есть свой.

      Удалить
    23. "В таких случаях обычно (исключения мне неизвестны) предпочтительнее использовать "табличный подход" к сохранению, заключающийся в том, что вначале сериализуется информация о структуре объекта [[, ], [, ], ...], а сами объекты сохраняются в виде списков значений [, ], а не словарей вида {: }."

      -- это "на самом деле" - уже "детали".

      Удалить
    24. "я занимаюсь ОБРАБОТКОЙ документов. БОЛЬШИХ"

      -- примеры документов - я привёл.

      Удалить
    25. Вы кстати знаете сколько страниц занимает Налоговый кодекс в напечатанном виде? А БИК?

      Удалить
    26. Ответил в личку :-)

      Удалить
    27. Я прочитал.. Вы - "читаете то что хотите прочитать".. Так "бывает"...

      Удалить
    28. Я написал "русски по-белому":

      "Но ПОВТОРЮ, что ОСНОВНАЯ мысль поста в том, что "с бинарной сериализацией" - лучше не связываться."

      ДАЛЬШЕ можно обсуждать dfm, xml, json, bjson и т.д. и т.п.

      Но это уже - "детали"... ДРУГИЕ "детали"... НЕ СВЯЗАННЫЕ с "бинарной сериализацией"...

      Надо ПОНЯТЬ ОДНО - есть "бинарная сериализация" и есть "другая сериализация"...

      Про это пост и был..

      ТОЛЬКО И ВСЕГО.

      Хотите обсуждать "другую сериализацию"?

      ДАВАЙТЕ.

      НО только - ОТДЕЛЬНО.

      Зачем и "что" я сериализую - ТОЖЕ давайте обсуждать ОТДЕЛЬНО.

      Поверьте - я 20-ть лет занимаюсь подобными проблемами - и судить о том, что "сноб" я или нет - не вам.. Я ДЕЛЮСЬ СВОИМИ "наработками"...

      КРИТИКОВАТЬ - критикуйте...

      Но не НАДО только заниматься"нравоучениями"...

      Или мы - "беседуем".. Или - НЕТ...

      Вы имеете СВОЮ точку зрения, Я - СВОЮ.

      У каждого есть "бэкграунд"..

      Посему - мы либо "беседуем", либо - НЕТ.

      Удалить
    29. И ещё.. Про RTTI...

      Мы похоже - ОБА знаем про RTTI - "более чем"...

      Так что - "не стоит об этом"...

      Но слова "В ЧАСТНОСТИ published" - Вы ТАК И НЕ ОБЪЯСНИЛИ..


      А если "не в частности"? Что ИМЕННО Вы имели ввиду?

      Поясню - в Delphi 7 - RTTTI оперирует ТОЛЬКО published свойствами. В Delphi XE* RTTI оперирует НЕ ТОЛЬКО published свойствами.

      Слово "в частности" - написали - ВЫ.

      Я вас к этому - НЕ ВЫНУЖДАЛ.

      Либо - "признайте СВОЮ ошибку", либо "мы говорим о XE*".

      Тот и другой варианты - они - ПЕРСПЕКТИВНЫ...

      Удалить
    30. И ещё...

      ПОЙМИТЕ ОДНУ простую вещь..

      Я ведь пишу не для ВАС...

      Вы и БЕЗ МЕНЯ - ПРЕКРАСНО всё понимаете.

      Я пишу для тех "миллионов", которые применяют "бинарную сериализацию" и другие "архаичные техники".

      Удалить
    31. Вы ведь МЕСТАМИ - "на голову выше" меня... Хотите "укусить" - кусайте.. Но зачем? Я ведь пишу не "для Вас", а для "миллионов", которые не "пережили архаичные техники"...

      Удалить
    32. Я за последние несколько месяцев увидел "не одного" сторонника "бинарной сериализации".... Вот и "считайте"..

      Удалить
    33. И ещё...

      Я 20-ть "с копейками" лет занимаюсь "обработкой документов"... Если МОЙ опыт - неинтересен - Ок, не буду писать о нём... Если же ИНТЕРЕСЕН, то не стоит "считать меня снобом"...

      Удалить
    34. Но! Если "опыт неинтересен" то ПИШИТЕ ПРО "свой опыт".. а не КРИТИКУЙТЕ меня.... Вы - ПИШИТЕ про "свой" опыт" - Я - про "свой"...

      Не КРИТИКОВАТЬ.. а ПИСАТЬ... Ок?

      Удалить
    35. Я одного не понял...
      Где (процитируйте) Вы увидели критику того, что у Вас?
      Я не знаю как у Вас (Вашего кода я не видел) - у меня просто нет предмета критики :-)
      Вы задавали вопросы, я на них отвечал.
      По поводу "в частности" тоже ответил.
      То, что было непонятно мне самому (зачем сейчас использовать именно "бинарную" и именно сериализацию) - спросил.

      Поясню - в Delphi 7 - RTTTI оперирует ТОЛЬКО published свойствами.
      RTTI как Вам известно, предоставляет информацию о типах во время выполнения, и использование этой информации не сводится только к работе с published-свойствами.
      Далее, контекст, в котором я использовал словосочетание "в частности", вызвавшее у Вас столько вопросов, был следующий:
      ««Во-вторых - вы про published или про "новый RTTI"?»
      -- Вообще-то я написал просто RTTI. Информация о типах во время выполнения позволяет получить почти всё необходимое для сериализации. В частности, поля отмеченные как published.»

      В System.Classes.TObject есть методы для определения адресов методов и полей объектов по имени, в TypInfo - функции преобразования enum-значений. Не уверен, что всё перечислил. Но всё это может быть использовано совершенно независимо от published-свойств, и за рамками сериализации. Ну и при сериализации/десериализации, разумеется, тоже может. Другой вопрос, как и зачем.

      «Либо - "признайте СВОЮ ошибку", либо "мы говорим о XE*".»
      -- Перед тем, как признавать ошибку, неплохо сначала понять, в чём она состоит ;-)

      «Я за последние несколько месяцев увидел "не одного" сторонника "бинарной сериализации".... Вот и "считайте"..»
      -- Мне было бы интересно узнать, какие соображения делают их сторонниками именно сериализации и именно "бинарной" её разновидности.

      «Но! Если "опыт неинтересен" то ПИШИТЕ ПРО "свой опыт"..»
      -- Да почему сразу "не интересен"... Непонятен — да, но если бы был неинтересен, стал бы я писать?
      То, что используется у нас - постарался подробно описать, когда говорил о сериализации "фабрик объектов".
      Альтернативный сериализации вариант с сохранением раздельно метаданных и данных - тоже.
      Сериализация ведь очень специфическая вещь. В силу необходимости восстановления ссылок между десериализуемыми объектами, приходится держать эти объекты в оперативной памяти. IMHO если объектов много, разумно озаботиться альтернативными сериализации механизмами обеспечения персистентности.
      Главным образом, поскольку (цитата из Википедии):
      «Любой из схем сериализации присуще то, что кодирование данных последовательно по определению, и извлечение любой части сериализованной структуры данных требует, чтобы весь объект был считан от начала до конца и воссоздан. Во многих приложениях такая линейность полезна, потому что позволяет использовать простые интерфейсы ввода-вывода общего назначения для сохранения и передачи состояния объекта. В приложениях, где важна высокая производительность, может иметь смысл использовать более сложную, нелинейную организацию хранения данных.»
      Говорю очевидные вещи? - не Вам, Александр, а "миллионам" о которых Вы упомянули.
      Никакой иронии. Честное слово.

      Удалить