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.
Input/Export Serialized Data
Exporting a Serialized Object
You may need to set the default coding by doing the following. The Core default doesn't seem to be readable.
xml_output_stream.default_text_encoding << :utf8
!ht << hash_table.new_with(:test,hash_table.new_with(:foo,"foo"))
serial_xml_thing.write_thing_to(!ht,"C:\temp\ht.xml")
Importing Serialized XML
serial_xml_thing.read_thing_from("C:\temp\ht.xml")
Read XML file
simple_xml.new(:attr_names_as_symbols?,_true).read_document_file("c:\my_file.xml")