Сериализация, сериализатор или еще один способ сохранения объектов в Java.

 

В программировании встречаются задачи, в которых требуется превращать объект или группу объектов в поток байт, для дальнейшего хранения, передачи по сети, через файлы, или каким-то другим способом. Если вы работаете не очень малый срок, то вам наверняка ,так или иначе, приходилось решать задачи подобного рода.

 

Процесс перевода какой-либо структуры данных в последовательность байт называют серилизацией.

Обратной операцией – десериализацией, называют восстановление объектов или группы из последовательности байт.

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

 

Не пускаясь в обширное объяснение теории на этот счет, лучше сразу перейти к практике.

Как известно именно аспект практического применения наиболее важен. Ведь для того что бы пользоваться самолетом совсем не обязательно досконально знать его конструкцию.

 

 

Практическое применение.

Итак для того что бы сохранить объект или группу в последовательность байт(и потом ее от туда восстановить) требуется :

 

1.  Зарегистрировать метод создания экземпляра класса.

2.  Зарегистрировать информацию о свойствах класса.

3.  Зарегистрировать информацию о работе со специфическими данными класса.

 

Для чего регистрируется метод создания экземпляра класса?

В момент превращения объекта класса в его бинарное представление (бинарник). В этом самом бинарнике нужно оставить данные о том, какой именно класс сохраняется, для того что бы потом, при выполнении обратной операции, можно было создать его объект.

Именно эту задачу и решает регистрация метода создания экземпляра класса. Если точнее при регистрации класса регистрируется некий функционал, который при передачи в него бинарного идентификатора класса создаст новый экземпляр.

Другими словами, регистрируя класс мы сопоставляем его договоренное символьное название (идентификатор) некоторой фабрике, которая создает его готовые экземпляры.

 

Эта операция выполняется при помощи метода regObjCreator интерфейса Registry

 

    Registry regObjCreator(InstanceCreator creator);

 

Сам объект Registry, как и все другие интерфейсы для работы, можно получить через интрефейс-фабрику  

 

RegistryFactory.INSTANCE.

 

 

 

 

В выше упоминается понятие свойства.

 

Так что же такое свойство?

Те, кто программировал на языках отличных от Java, например C++Builder или Delphi прекрасно знают ответ на этот вопрос.  

Свойство — это просто имя, скрывающее путь, которым вы получаете доступ к данным или методу. Свойство может работать на прямое чтение или запись данных через поле, а может ссылаться на метод, обеспечивающий доступ.

 

Здесь, в данном программном компоненте, свойством называется информация о классе, которая позволяет, прямо или косвенно, прочитать и\или установить значения полей его экземпляров. Впрочем, данное определение не отменяет предыдущего.

 

Типы свойств можно разделять по типам данных этих свойств и по возможности установки.

 

Типы свойств по возможности установки.

1.1. RO - Read Only property свойство только для чтения.

1.2. RW - Read Write property свойство, как для чтения, так и для записи.

 

RO - означает, что это свойство можно только прочитать у объекта, такой тип используется только для того чтобы получить указатель на объект, у этого внутреннего объекта, в свою очередь,  можно тоже записать или прочитать свойства. Свойство, которое находится внутри другого свойства, называется «вложенным». Свойство, которое может содержать другое свойство, называется «содержащим».

 

 

RW означает, что свойство может быть прочитано и установлено. Такой тип свойств не считается содержащим.

 

Также, в принципе мог бы быть описан и третий тип – WriteOnly(WO) свойство, но этот тип мы намеренно не рассматриваем и не поддерживаем, для того что бы не делать сложным пользовательский интерфейс.

 

 

Самым простым способом регистрации свойства является :

 

Registry.ClassPropRegistrySlot(RegisteredClass.class).regProp("propertyOne  field Method()")

 

ClassPropRegistrySlot slot = Registry.ClassPropRegistrySlot(RegisteredClass.class);

slot.regProp("propertyOne    someField                  someField");

slot.regProp("propertyTwo    someField                 someWriteMethod()");

slot.regProp("propertyThree  someField                 someMethod()");

slot.regProp("propertyFour    someReadMethod()  someWriteMethod()");

 

Ниже представлены примеры регистрации таким  простым способом,

 

"propertyOne  someField        someField"

 

Первым элементом данной строки является название свойства.

вторым и третьим может стоять либо наименование поля, либо наименование метода с добавленными скобками “()” для того чтобы делались различия между методом и полем.

 

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

Дополнительная регистрация нужна для тех случаев, если требуется объявить некое свойство у класса, но для этого нет, ни подходящих полей, ни подходящих методов.

Для этого у интерфейса ClassPropRegistrySlot имеется набор методов, позволяющих зарегистрировать такие пользовательские свойства.

 

    ClassPropRegistrySlot regProp(String propertyName, LongProperty property);

    ClassPropRegistrySlot regProp(String propertyName, BinaryProperty property);

    ClassPropRegistrySlot regProp(String propertyName, IntegerProperty property);

    ClassPropRegistrySlot regProp(String propertyName, ByteProperty property);

    ClassPropRegistrySlot regProp(String propertyName, ShortProperty property);

    ClassPropRegistrySlot regProp(String propertyName, StringProperty property);

    ClassPropRegistrySlot regProp(String propertyName, CharProperty property);

 

Первым параметром каждого такого метода идет  название свойства, вторым переданный интерфейс получения и

установки значений идет свойства.

 

ClassPropRegistrySlot это интерфейс информации о свойствах класса, его можно получить способом, показанным ниже.

RegistryPair pair RegistryFactory.INSTANCE.getRegistryPair();

(Registry registry = pair.getRegistry())

ClassPropRegistrySlot slot = registry.classProps(Ancestor.class);

 

 

Виды свойств.

Свойства объекта можно разделить на три вида по их природе.

 

1. Простое свойство(Simple property)

К простым свойствам относятся:

  1. Все примитивные типы  char, byte, short ,integer, long, float, double, все перечисления (enum).
  2. Объектовые аналоги примитивных типов Char, Byte, Short, Integer, Long, Float, Double.
  3. Строки. (String)
  4. Свойство такого типа  ВСЕГДА может быть установлено и прочитано (всегда является Read Write property).
  5. Специальные типы зарегистрированные как простые.

 

 

2. Содержащее свойство (Containing property), уже упоминалось ранее.

Содержащее свойство отличается от простого свойства тем, что оно:

  1. Всегда RO.
  2. Тип его значения не является  ни одним из типов простого свойства.
  3. Может содержать или содержит другие свойства.

 

 

3. Ссылочное свойство(Reference property)

Ссылочное свойство это свойство, которое не принадлежит ни к первому, ни ко второму виду и фактически представляет собой ссылку на объект. Следует учитывать, что такое свойство не может ссылаться на любой объект. Это еще одно естественное ограничение, связанное с тем, что при сохранении записывается уникальный символьный идентификатор объекта, на который свойство ссылается, при выполнении процесса - вычитки производится процедура обратная и по символьному идентификатору находится соответствующий ему объект. Но, так как,  не все объекты в сохраняемой группе могут быть поименованы, и сохранить ссылки на все объекты нельзя.  

 

 

Важным моментом является правило того как определяется к какому виду принадлежит то или иное свойство.

От этого напрямую зависит, как с этим свойством будет проводиться дальнейшая работа.

 

Правила формирования свойств.

Вид свойства явно не описывается каким-то специальным образом при регистрации и вычисляется следующим способом.

Сначала проверяется, является ли свойство простым.

Потом, если свойство не простое, является ли свойство содержащим.

Последним шагом проверяется, является ли свойство ссылочным

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

 

 

Списки.

Немаловажным моментом является такая часто требуемая вещь как сохранение и восстановление списков.

Здесь списком называется упорядоченная конечная последовательность определенных(не null)элементов одинакового типа.

Для того что бы зарегистрировать некоторое свойство как списочное требуется вызвать метод regProp  интерфейса ClassPropRegistrySlot для этого класса, в который передается единственный параметр интерфейс ListedItem. Этот интерфейс и регламентирует поведение при сохранении и вычитке элементов.

 

    ClassPropRegistrySlot regProp(ListedItem property);

 

 

 

Что следует заметить о чтении и записи в целом.

Запись реализуется более простым алгоритмом чем чтение, это

обусловлено тем, что в момент записи абсолютно все объекты их свойства, являются "живыми", это позволяет считать их из объектов и спокойно записать в бинарник.

 

Чтение является более сложным и более затратным в силу ряда причин.

Одной из причин является та, что на момент создания одного объекта, второго, на который он ссылается, может еще не существовать. Вторая сложность заключается в том, что данные сохраненные одними зарегистрированными свойствами могут быть прочитаны уже другими. Эта ситуация не редка и вызвана тем что код приложения со временем изменяется. Если в процессе развития приложения у свойства изменяется тип, то при вычитке старых данных они будут приводиться к новому типу, если такое преобразование невозможно генерируется ошибка чтения.

 

 

В общем и целом, для того что бы сохранить и затем восстанавливать объект требуется сделать

3 шага

  1. Один раз зарегистрировать информацию о свойствах.
  2. Передать объект(или группу) в функционал записи и получить снятый с него бинарник.
  3. Передать бинарник в функционал чтения, получить объект на выходе.

 

Можно каким угодно способом хранить или пересылать данные между шагами 2 и 3.

 

На данный момент имеется реализация данной технологии с сохранением данных объекта в XML, готовится к выпуску формат более быстрого и компактного сохранения.

Что касается XML, то сохраненная группа объектов  выглядит примерно так.

 

 

<obj clsid="ObjRoot" name="ObjRoot238">

  <prop path="AnotherString" value="DDT" />

  <obj clsid="ObjOne" name="ObjOne234" pos="0">

    <prop path="IntValue" value="-1364251215" />

    <prop path="FloatVal" value="0.90201235" />

    <prop path="StringVal" value="[6tv4,k5z2zxg9[d[vd0jweu" />

    <prop path="ObjOneRef" value="ObjOne234" />

    <obj clsid="ObjOne" name="ObjOne261" pos="0">

      <prop path="IntValue" value="-2082278118" />

      <prop path="FloatVal" value="0.4154616" />

      <prop path="StringVal" value="fzxrqz.mo;4g7e5" />

      <prop path="ObjOneRef" value="ObjOne261" />

      <obj clsid="ObjOne" name="ObjOne283" pos="0">

        <prop path="IntValue" value="-1964927930" />

        <prop path="FloatVal" value="0.06526089" />

        <prop path="StringVal" value="o.pnc9f9y3nc,ee25h" />

        <prop path="ObjOneRef" value="ObjOne261" />

      </obj>

      <obj clsid="ObjOne" name="ObjOne247" pos="1">

        <prop path="IntValue" value="1127172642" />

        <prop path="FloatVal" value="0.36717206" />

        <prop path="StringVal" value="sotnc[2u64p;.rfsahdgo.e" />

        <prop path="ObjOneRef" value="ObjOne261" />

      </obj>

      <obj clsid="ObjOne" name="ObjOne297" pos="2">

        <prop path="IntValue" value="-1818092306" />

        <prop path="FloatVal" value="0.049372137" />

        <prop path="StringVal" value="c6rr7jt5q;y7m," />

        <prop path="ObjOneRef" value="ObjOne283" />

      </obj>

    </obj>

    <obj clsid="ObjOne" name="ObjOne226" pos="1">

      <prop path="IntValue" value="1569494093" />

      <prop path="FloatVal" value="0.81084085" />

      <prop path="StringVal" value="ente0nh=7,us.d964svz17y" />

      <prop path="ObjOneRef" value="ObjOne297" />

      <obj clsid="ObjOne" name="ObjOne361" pos="0">

        <prop path="IntValue" value="-1157020145" />

        <prop path="FloatVal" value="0.1741072" />

        <prop path="StringVal" value="cgs8n8=rsdvfs610v;,sa" />

        <prop path="ObjOneRef" value="ObjOne297" />

      </obj>

      <obj clsid="ObjOne" name="ObjOne347" pos="1">

        <prop path="IntValue" value="-22782595" />

        <prop path="FloatVal" value="0.70589924" />

        <prop path="StringVal" value="gau=g,-y.mjyiy1." />

        <prop path="ObjOneRef" value="ObjOne361" />

      </obj>

    </obj>

  </obj>

  <obj clsid="ObjOne" name="ObjOne238" pos="1">

    <prop path="IntValue" value="-1951274415" />

    <prop path="FloatVal" value="0.41215885" />

    <prop path="StringVal" value="6pjghv695m" />

    <prop path="ObjOneRef" value="ObjOne234" />

    <obj clsid="ObjOne" name="ObjOne27" pos="0">

      <prop path="IntValue" value="745078304" />

      <prop path="FloatVal" value="0.9565979" />

      <prop path="StringVal" value="[k308=5c1z=wpo=qu-a-qpz,jec1" />

      <prop path="ObjOneRef" value="ObjOne27" />

      <obj clsid="ObjOne" name="ObjOne272" pos="0">

        <prop path="IntValue" value="132402895" />

        <prop path="FloatVal" value="0.5060047" />

        <prop path="StringVal" value="=wf.9ml7j0indr-quqidq" />

        <prop path="ObjOneRef" value="ObjOne226" />

      </obj>

      <obj clsid="ObjOne" name="ObjOne21" pos="1">

        <prop path="IntValue" value="1680954855" />

        <prop path="FloatVal" value="0.5556625" />

        <prop path="StringVal" value="x9j2vk4x=xc0;aba04ri,gc4" />

        <prop path="ObjOneRef" value="ObjOne226" />

      </obj>

    </obj>

    <obj clsid="ObjOne" name="ObjOne257" pos="1">

      <prop path="IntValue" value="1388175525" />

      <prop path="FloatVal" value="0.42612046" />

      <prop path="StringVal" value="[qaeu,497lcqx8" />

      <prop path="ObjOneRef" value="ObjOne361" />

      <obj clsid="ObjOne" name="ObjOne284" pos="0">

        <prop path="IntValue" value="-460519327" />

        <prop path="FloatVal" value="0.55407125" />

        <prop path="StringVal" value="b,5ppq72qn42bf5x9zddq3s12b.0s" />

        <prop path="ObjOneRef" value="ObjOne347" />

      </obj>

      <obj clsid="ObjOne" name="ObjOne249" pos="1">

        <prop path="IntValue" value="-499459161" />

        <prop path="FloatVal" value="0.2980135" />

        <prop path="StringVal" value="2,tk;wfrh57d4mpc0ip09" />

        <prop path="ObjOneRef" value="ObjOne247" />

      </obj>

      <obj clsid="ObjOne" name="ObjOne266" pos="2">

        <prop path="IntValue" value="-407449704" />

        <prop path="FloatVal" value="0.6124093" />

        <prop path="StringVal" value="4lfgcu,7.11dtbfkoc" />

        <prop path="ObjOneRef" value="ObjOne21" />

      </obj>

    </obj>

    <obj clsid="ObjOne" name="ObjOne237" pos="2">

      <prop path="IntValue" value="-1895249275" />

      <prop path="FloatVal" value="0.65914905" />

      <prop path="StringVal" value="rwka.wjt;d46gq" />

      <prop path="ObjOneRef" value="ObjOne21" />

      <obj clsid="ObjOne" name="ObjOne263" pos="0">

        <prop path="IntValue" value="83978294" />

        <prop path="FloatVal" value="0.06265342" />

        <prop path="StringVal" value="6hzs.lq8,.den[f3gs4" />

        <prop path="ObjOneRef" value="ObjOne266" />

      </obj>

      <obj clsid="ObjOne" name="ObjOne246" pos="1">

        <prop path="IntValue" value="-1979078211" />

        <prop path="FloatVal" value="0.52389294" />

        <prop path="StringVal" value="8t7b98m63ao" />

        <prop path="ObjOneRef" value="ObjOne347" />

      </obj>

    </obj>

  </obj>

</obj>

 

Что вполне читаемо человеком !!

 

 

 

 

Приложение с демонстрацией возможностей технологии находится тут.

 

 

 

 

 

Вот, наверное, и все.

Детальное описание возможностей имеется в исходниках классов библиотеки.

 

Если дорогой читатель дочитал до этой строки и не бросил это занятие ранее, значит, вероятно, данная статья вызвала определенный интерес.

В представленном приложении демонстрируются возможности данной технологии.

Хотя на данный момент уже имеется более 50 примеров различных ситуаций, этого может быть недостаточно для выявления всех деталей(хороших и не очень).

Поэтому, полагая, что несколько голов это лучше чем одна, предлагаем  и вам, добавить что то свое.

Нас, в первую очередь интересуют те ситуации, в которых сохранение или чтение работает не в соответствии с предполагаемым описанным поведением(то есть ,собственно, ошибочное поведение).

Вы можете дополнить существующее демонстрационное приложение.

Для того что бы это сделать.

В своем  пакете напишите класс c публичным конструктором без параметров в этом классе опишите нестатические публичные методы без параметров, которые ничего не возвращают.

@LightTest
public void test3() {

//  Напишите внутри метода свой собственный код теста.

}

 

Процесс регистрации примеров на этом и заканчивается, что бы он был выполнен поместите ваш пакет с классами в каталог приложения addons.

 

И на этом все. Далее у MainDemo нужно вызвать метод main.

для этого есть 3 возможности.

вызвать имеющиеся BAT файлы

run.bat, runwriteerrors.bat или runreaderrors.bat.

run.bat - выполняет все тесты.

runwriteerrors.bat выполняет все тесты, имена методов с ошибками записываются в файл methods.txt

runreaderrors.bat выполняет только те методы тестов, которые описаны в methods.txt.

 

 

Если из примеров что-то остается непонятным, будем рады ответить на ваши вопросы.

 

 

Ваши примеры нам очень важны (с логикой, которая помогает выявить неправильное выполнение, особенно). Присылайте ответы на адрес указанный в контактной информации. В письме укажите тему письма "Serialization". 

 

После проверки и, если это потребуется, исправления примеры попадут в обновленную версию демонстрационного приложения.

 

Ограничения по использованию продукта описаны тут.

 

Приложение с демонстрацией возможностей технологии находится тут.

А тут вы можете дать на уголь для нашего паровоза.

Контакты

E-mail: steammachine@yandex.ru

© SteamMachine

Создать бесплатный сайт с uCoz