In object-oriented programming (OOP), the principle of encapsulation is understood as object being a holder of state contained within its properties. A related issue is how to preserve that state when object goes out of memory, such as between program executions.

Such external persistence of object state is commonly referred to serialization, an ability to store the object state in an external storage and later recreate the object from that storage.

It is interesting to note that J already has native persistence mechanism as used in configuration files.

Because of object-oriented nature, serialization is different from flat file or record based persistence, applying the same encapsulation principle in a way that an object "knows" how to serialize itself. On the other hand, discovering the object features through common interface, the system can homogenously serialize lists and hierarchies of objects, leveraging another principle of polymorphism.

Serialization was one of the most compelling use cases when OOP was popularized with the relases of Turbo Pascal 5.5 back in 1989. There each object registered with stream having and implemeted method Store and constructor(!) Load to store itself on a stream, whereas stream had Put and Get methods, such that Put stored a class identifier and Get initialized an object from stream with its Load constructor. As a result it has been very convenient to have any kind of hierarchy in memory, and then simply zoom and dump it all on disk. Although all serializer methods had to be implemented manually.

Now in frameworks like Java and .NET reflection is used to automate the serialization and descriptive attributes to declare which attributes and how are affected.

Here we will look at how hierarchical object serialization can be represented in J, where dynamically typed and interpreted environment can be leveraged.

We start with an example of typical use cases for property serialization.

«example.ijs»=
require 'costream myclasses'

'Root'cobegin'Class1'        NB. or locally r=. cobegin''conew'Class1'
  PROP1=: 1
  PROP2=: <'!@#'
  'OBJPROP1'cobegin'Class2'
    PROP4=: 4 5 6
    LIT1=: 'one two'
    LIT2=: 0 : 0
werwerwer
werwerwer
)
    LIT3=: }:0 : 0
werwerwer
werwerwer
)
    'LIST1'cobegin'Class3'
      P1=: 1
      P2=: 2
    coend''
    'LIST1'coadd'Class3'
      P1=: 1
    coend''
    'LIST1'coadd'Class3'
      P2=: 1
    coend''
  coend''
coend''

Here the following features are illustrated

With such format we attain sevaral goals

Classes that are instantiated are defined in a separate script.

«myclasses.ijs»= arbitrary classes with "create"
coclass'Class1'
  COREP=: ;:'PROP1 PROP2 _OBJPROP1'
create=: 3 : 0
  TYPE=: 'Class1'
)

coclass'Class2'
  COREP=: ;:'PROP4 LIT1 LIT2 LIT3 _LIST1'
create=: 3 : 0
  TYPE=: 'Class2'
  PROP5=: 'unused'
)

coclass'Class3'
create=: 3 : 0
  y
)

Finally, the syntax sugar verbs to define the locale switching brackets are defined in a colib-complementary script

«costream.ijs»=
NB. costream - object serialization

coclass 'z'

cobegin=: ([ cocurrent f.)@(3 : 0)
  if. 0>nc <'COSTACK_j_' do. COSTACK_j_=: '' end.
  COSTACK_j_=: COSTACK_j_,coname''
  y
:
  cobegin (x)=: '' conew y
)

coadd=: ([ cocurrent f.)@(4 : 0)
  cobegin {:(x)=: x~,'' conew y
)

coend=: ([ cocurrent f.)@(3 : 0)
  w=. {:COSTACK_j_
  COSTACK_j_=: }:COSTACK_j_
  w
)

NB. corep v locale representation as string
NB.   str=. 'RootName' corep Root
corep=: 3 : 0
  'Root' corep y
:
  r=. ''
  if. 0>nc <'COREPIND_j_' do. COREPIND_j_=: '' end.      NB. global recursive indent
  if. a: -: c=. {.(copath ::(a:"_) y)-.<,'z' do.
     COREPIND_j_,x,'=: ',(5!:5<'y'),LF return.           NB. if not object represent as value
  end.
  if. '+'={.x do. x=.}.x[s=.'add' else.s=.'begin'end.    NB. for list items use coadd
  r=. r,COREPIND_j_,'''',x,'''co',s,'''',(>c),'''',LF    NB. cobegin
  COREPIND_j_=: COREPIND_j_,'  '
  if. a=.0>nc <'COREP__y' do. l=.nl__y'' else. l=.COREP__y end.
  l=. l-.<'COCREATOR'
  for_i. l do.
    m=. >i
    n=. m-.'_'
    p=. n,'__y'
    if. 0>nc< p do. continue. end.
    v=. p~
    if. nc< p do.
      r=. r,COREPIND_j_,n,'=: ',(5!:5 < p),LF
    elseif. ('_'={.m) do.                              NB.  +.a*.((,1)-:~.;#@$&.>v)*.(2>#@$v)*.1=L.v
      r=. r,n corep {.v
      for_j. }.v do.
        r=. r,('+',n) corep j
      end.
    elseif. 1 do.
      r=. r,COREPIND_j_,n,'=: '
      if.  0:`((10&e. *. 31&< *. <&127)@:(a.&i.))@.((0<#)*.2=3!:0) v do.
        r=. r,(k#'}:'),'0 : 0',LF,v,(LF#~k=.LF~:{:v),')',LF
      else.
        r=. r,(5!:5 < p),LF
      end.
    end.
  end.
  COREPIND_j_=: _2}.COREPIND_j_
  r=. r,COREPIND_j_,'coend''''',LF                       NB. coend
)

The verb corep above gerates string representation similar to example.ijs above:

   corep Root
'Root'cobegin'Class1'
  'OBJPROP1'cobegin'Class2'
    PROP4=: 4 5 6
    LIT1=: 'one two'
...
coend''

See Also


CategoryWorkInProgress

Scripts/Costream (last edited 2009-09-08 08:36:37 by OlegKobchenko)