Serialization of Magik

How to serialize an object?

Sometimes you want to store an object to a file of stream and recreate it later again. Perhaps you like the JSON.serialize function and want it in Magik too. Well, in Magik it’s a little more complex than in javascript, but it works. I have created a simple exemplar to show the 4 steps.

1) First let the exemplar inherit from :serial_structure_mixin.

_pragma(classify_level=basic, topic={demo})
def_slotted_exemplar(:trace_result,
    {
        {:average, _unset},
        {:description, _unset}
    },
    {:serial_structure_mixin})

2) Define the constant serial_structure. It should return the type of structure of self. Typically the answer is :slotted because often the exemplar is a slotted exemplar. Read the class comment of serial_structure_mixin for other possible values.
trace_result.define_shared_constant(:serial_structure, :slotted, :public)

3) Define the method serial_slots() to return the names and values that need serialization.
_pragma(classify_level=basic, topic={demo})
_method trace_result.serial_slots()
    _local keys   << {:average, :description}
    _local values << {.average, .description}
    _return keys, values
_endmethod

4) Define the method new_from_serial(keys,values) to instantiate an object from serialization.
_pragma(classify_level=basic, topic={demo})
_method trace_result.new_from_serial(keys,values)
    _return _clone.init_from_serial(_scatter values)
_endmethod
$

_pragma(classify_level=basic, topic={demo})
_private _method trace_result.init_from_serial(average, description)
    .average << average
    .description << description
    _return _self 
_endmethod
$

Now we can test this on the prompt. The example file is attached if you want to try it.

s << serial_xml_thing.thing_as_xml_string(trace_result.new_demo())
$
"<?xml version="1.0" encoding="ISO-10646-UCS-2" standalone="yes"?><newline><serial_xml_thing format="1"><newline><tab><slotted class="user:trace_resu"... 
MagikSF> x << serial_xml_thing.xml_string_as_thing(s)
$
a trace_result 
MagikSF> print(!)
$
trace_result (:slotted )
trace_result!average    12.43 
trace_result!description    "This is a cereal demo" 
MagikSF>

records

This all looks simple. It gets more complicated when database records are involved because they have no default way of serialization. There are many ways to deal with this. A nice way is to use proxy objects that wrap the records. The proxy objects do know how to serialize.

As an example I have created another exemplar (trace_result_with_records) that holds a slot with a rope of roundabouts records (from the Cambridge demo database).
The serialization is done like this:

_pragma(classify_level=basic, topic={demo})
_method trace_result_with_records.serial_slots()
    _local keys   << {:average, :description, :roundabouts}
    _local values << {.average, .description, _self.serial_roundabouts}
    _return keys, values
_endmethod
$

_pragma(classify_level=basic, topic={demo})
_private _method trace_result_with_records.serial_roundabouts
    _local list << rope.new_for(.roundabouts.size)
    _for i_rec _over .roundabouts.fast_elements()
    _loop
        list.add(serial_record_lookup.new(i_rec))
    _endloop
    _return list 
_endmethod
$
_pragma(classify_level=basic, topic={demo})
_method trace_result_with_records.new_from_serial(keys,values)
    _return _clone.init_from_serial(_scatter values)
_endmethod
$

_pragma(classify_level=basic, topic={demo})
_private _method trace_result_with_records.init_from_serial(average, description, roundabouts)
    .average << average
    .description << description
    .roundabouts << roundabouts
    _return _self 
_endmethod
$

The execution of the serialization of the roundabouts in serial_slots() is delegated to the serial_record_lookup record. The lookup record will return the original database record from the serialization. That is why the init_from_serial() method does not have to convert from serial_record_lookup to roundabout.

The serial_record_lookup sourcecode looks like this:

_pragma(classify_level=basic, topic={demo})
def_slotted_exemplar(:serial_record_lookup,
    {
        {:urn, _unset}
    },
    {:serial_structure_mixin})
$

_pragma(classify_level=basic, topic={demo})
_method serial_record_lookup.new(p_ds_record)
    _return _clone.init(p_ds_record)
_endmethod
$

_pragma(classify_level=basic, topic={demo})
_private _method serial_record_lookup.init(p_ds_record)
    .urn << urn_manager.get_urn_from_object(p_ds_record)
    _return _self 
_endmethod
$

###############
# SERIAL METHODS
###############
$

serial_record_lookup.define_shared_constant(:serial_structure, :slotted, :public)
$

_pragma(classify_level=basic, topic={demo})
_method serial_record_lookup.serial_slots()
    _return {:urn}, {.urn}
_endmethod
$

_pragma(classify_level=basic, topic={demo})
_method serial_record_lookup.new_from_serial(keys,values)
    _local urn << values[1]
    _return urn_manager.get_object_from_urn(urn)
_endmethod
$

I think a simple an elegant solution. The complete code is attached here sources. Enjoy.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License