Object serialization: Difference between revisions
m →{{header|Ruby}}: brief intro |
|||
Line 398:
{
self = [super init];
animalName = nil;▼
return self;
}
Line 408 ⟶ 406:
numberOfLegs = legs;
return self;
}
- (void) dealloc
{
[super dealloc];
}
- (void) dump
Line 459 ⟶ 462:
- (void) addEatenThing: (NSString*)thing
{
[eatenList addObject: thing];
}
- (void) dealloc
{
[eatenList release];
[super dealloc];
}
- (void) dump
Line 525 ⟶ 532:
// now let us store the objects...
NSMutableData *data = [[NSMutableData
NSKeyedArchiver *arch = [[NSKeyedArchiver alloc]
initForWritingWithMutableData: data];
[arch encodeObject: anAnimal forKey: @"Eptohippos"];
[arch encodeObject: aMammal forKey: @"Mammaluc"];
[anAnimal release];
[aMammal release];
[arch finishEncoding];
[arch release];
[data writeToFile: @"objects.dat" atomically: YES];
[data release];
// now we want to retrieve the saved objects...
Line 542 ⟶ 553:
[darch finishDecoding];
[ldata release];
[darch release];
// now let's dump/print the objects...
|
Revision as of 10:10, 13 August 2009
You are encouraged to solve this task according to the task description, using any language you may know.
Create a set of data types based upon inheritance. Each data type or class should have a print command that displays the contents of an instance of that class to standard output. Create instances of each class in your inheritance hierarchy and display them to standard output. Write each of the objects to a file named objects.dat in binary form using serialization or marshalling. Read the file objects.dat and print the contents of each serialized object.
Ada
This file contains the package specification containing the public definitions of the inheritance tree rooted at the type Message. Each type in the inheritance tree has its own print procedure. <lang ada>with Ada.Calendar; use Ada.Calendar;
package Messages is
type Message is tagged record Timestamp : Time; end record; procedure Print(Item : Message); procedure Display(Item : Message'Class);
type Sensor_Message is new Message with record Sensor_Id : Integer; Reading : Float; end record; procedure Print(Item : Sensor_Message); type Control_Message is new Message with record Actuator_Id : Integer; Command : Float; end record; procedure Print(Item : Control_Message);
end Messages;</lang>
The next portion contains the implementation of the procedures defined in the package specification. <lang ada>with Ada.Text_Io; use Ada.Text_Io; with Ada.Integer_Text_Io; use Ada.Integer_Text_Io; with Ada.Float_Text_IO; use Ada.Float_Text_IO;
package body Messages is
----------- -- Print -- -----------
procedure Print (Item : Message) is The_Year : Year_Number; The_Month : Month_Number; The_Day : Day_Number; Seconds : Day_Duration; begin Split(Date => Item.Timestamp, Year => The_Year, Month => The_Month, Day => The_Day, Seconds => Seconds); Put("Time Stamp:"); Put(Item => The_Year, Width => 4); Put("-"); Put(Item => The_Month, Width => 1); Put("-"); Put(Item => The_Day, Width => 1); New_Line; end Print;
----------- -- Print -- -----------
procedure Print (Item : Sensor_Message) is begin Print(Message(Item)); Put("Sensor Id: "); Put(Item => Item.Sensor_Id, Width => 1); New_Line; Put("Reading: "); Put(Item => Item.Reading, Fore => 1, Aft => 4, Exp => 0); New_Line; end Print;
----------- -- Print -- -----------
procedure Print (Item : Control_Message) is begin Print(Message(Item)); Put("Actuator Id: "); Put(Item => Item.Actuator_Id, Width => 1); New_Line; Put("Command: "); Put(Item => Item.Command, Fore => 1, Aft => 4, Exp => 0); New_Line; end Print;
------------- ---Display -- ------------- procedure Display(Item : Message'Class) is begin Print(Item); end Display;
end Messages;</lang>
The final section of code creates objects of the three message types and performs the printing, writing, and reading. The Ada attributes 'Class'Output serialize the object and write it to the specified stream. The 'Class'Input attributes call a function automatically provided by the compiler which reads from the specified stream file and returns the object read. The Display procedure takes an object in the inheritance tree rooted at Message and dispatches the correct print procedure.
<lang ada>with Messages; use Messages; with Ada.Streams.Stream_Io; use Ada.Streams.Stream_Io; with Ada.Calendar; use Ada.Calendar; with Ada.Text_Io;
procedure Streams_Example is
S1 : Sensor_Message; M1 : Message; C1 : Control_Message; Now : Time := Clock; The_File : Ada.Streams.Stream_Io.File_Type; The_Stream : Ada.Streams.Stream_IO.Stream_Access;
begin
S1 := (Now, 1234, 0.025); M1.Timestamp := Now; C1 := (Now, 15, 0.334); Display(S1); Display(M1); Display(C1); begin Open(File => The_File, Mode => Out_File, Name => "Messages.dat"); exception when others => Create(File => The_File, Name => "Messages.dat"); end; The_Stream := Stream(The_File); Sensor_Message'Class'Output(The_Stream, S1); Message'Class'Output(The_Stream, M1); Control_Message'Class'Output(The_Stream, C1); Close(The_File); Open(File => The_File, Mode => In_File, Name => "Messages.dat"); The_Stream := Stream(The_File); Ada.Text_Io.New_Line(2); while not End_Of_File(The_File) loop Display(Message'Class'Input(The_Stream)); end loop; Close(The_File);
end Streams_Example;</lang> Output results:
Time Stamp:2007-3-9 Sensor Id: 1234 Reading: 0.0250 Time Stamp:2007-3-9 Time Stamp:2007-3-9 Actuator Id: 15 Command: 0.3340 Time Stamp:2007-3-9 Sensor Id: 1234 Reading: 0.0250 Time Stamp:2007-3-9 Time Stamp:2007-3-9 Actuator Id: 15 Command: 0.3340
ALGOL 68
Serialization in ALGOL 68 is achieved through a technique called straightening.
MODE ENTITY = STRUCT([6]CHAR name, INT creation); FORMAT entity repr = $"Name: "g", Created:"g$; MODE PERSON = STRUCT(ENTITY entity, STRING email); FORMAT person repr = $f(entity repr)", Email: "g$; PERSON instance1 := PERSON(ENTITY("Cletus", 20080808), "test+1@localhost.localdomain"); print((name OF entity OF instance1, new line)); ENTITY instance2 := ENTITY("Entity",20111111); print((name OF instance2, new line)); FILE target; INT errno := open(target, "rows.dat", stand back channel); # open file # # Serialize # put(target,(instance1, new line, instance2, new line)); printf(($"Serialized..."l$)); close(target); # flush file stream # errno := open(target, "rows.dat", stand back channel); # load again # # Unserialize # PERSON i1; ENTITY i2; get(target,(i1, new line, i2, new line)); printf(($"Unserialized..."l$)); printf((person repr, i1, $l$)); printf((entity repr, i2, $l$))
FLEXible length arrays (including STRINGs), and tagged-UNION types are problematic as the lengths of the arrays, and the tag of the union is not stored. Sometimes a FORMAT can be manually created to handle these lengths and tags. Also note that ALGOL 68 is strongly types and the type (mode) of the objectis note stored, but compiled into the code itself.
Output:
Cletus Entity Serialized... Unserialized... Name: Cletus, Created: +20080808, Email: test+1@localhost.localdomain Name: Entity, Created: +20111111
Common Lisp
<lang lisp>(defmacro with-serialization-to-file ((stream pathname) &body body)
`(with-open-file (,stream ,pathname :element-type '(unsigned-byte 8) :direction :output :if-exists :supersede) ,@body))
(defclass entity ()
((name :initarg :name :initform "Some entity")))
(defclass person (entity)
((name :initarg :name :initform "The Nameless One")))</lang>
And now the REPL log:
<lang lisp>CL-USER> (list (make-instance 'entity)
(make-instance 'person))
(#<ENTITY {1004B13141}> #<PERSON {1004B142B1}>) CL-USER> (mapc #'describe *)
- <ENTITY {1004B13141}>
[standard-object]
Slots with :INSTANCE allocation:
NAME = "Some entity"
- <PERSON {1004B142B1}>
[standard-object]
Slots with :INSTANCE allocation:
NAME = "The Nameless One"
(#<ENTITY {1004B13141}> #<PERSON {1004B142B1}>) CL-USER> (with-serialization-to-file (stream "/tmp/objects.dat")
(cl-serializer:serialize * :output stream) ;; SERIALIZE shows an octet-vector as its return value (values))
- No value
CL-USER> (mapc #'describe (with-open-file (stream "/tmp/objects.dat"
:element-type '(unsigned-byte 8)) (cl-serializer:deserialize stream)))
- <ENTITY {1003C12911}>
[standard-object]
Slots with :INSTANCE allocation:
NAME = "Some entity"
- <PERSON {1003C12A81}>
[standard-object]
Slots with :INSTANCE allocation:
NAME = "The Nameless One"
(#<ENTITY {1003C12911}> #<PERSON {1003C12A81}>)</lang>
E
(Inheritance, while supported by various features and patterns, is not a preferred design component in E; nor are simple record data structures.)
def makeEvent(time :int) { return def event { to __printOn(out) { out.print(`@@$time`) } to __optUncall() { return [makeEvent, "run", [time]] } to getTime() { return time } } } def makeArrival(time :int, what :any, position :int) { return def arrival extends makeEvent(time) { to __printOn(out) { out.print(`$what to $position $super`) } to __optUncall() { return [makeArrival, "run", [time, what, position]] } to getWhat() { return what } to getPosition() { return position } } }
After defining our data types, we can prepare to serialize them.
def surgeon := <import:org.erights.e.elib.serial.makeSurgeon>().diverge() surgeon.addExit(makeEvent, "makeEvent") surgeon.addExit(makeArrival, "makeArrival")
The 'exits' of the surgeon (so called because it cuts and joins object subgraphs) specify the points at which serialization should stop, instead replacing references to the objects with the specified names. On unserialization, the names are looked up and replaced with the corresponding objects. (The surgeon provides default exits for such things as false, true, null, and the list constructor.)
def objs := [makeEvent(timer.now()), makeArrival(timer.now(), "Smith", 7)] stdout.println(objs) <file:objects.dat>.setBytes(surgeon.serialize(objs)) stdout.println(surgeon.unserialize(<file:objects.dat>.getBytes()))
Java
<lang java>import java.io.*;
// classes must implement java.io.Serializable in order to be serializable class Entity implements Serializable {
// it is recommended to hard-code serialVersionUID so changes to class // will not invalidate previously serialized objects static final long serialVersionUID = 3504465751164822571L; String name = "Entity"; public String toString() { return name; }
}
class Person extends Entity implements Serializable {
static final long serialVersionUID = -9170445713373959735L; Person() { name = "Cletus"; }
}
public class SerializationTest {
public static void main(String[] args) { Person instance1 = new Person(); System.out.println(instance1);
Entity instance2 = new Entity(); System.out.println(instance2);
// Serialize try { ObjectOutput out = new ObjectOutputStream(new FileOutputStream("objects.dat")); // open ObjectOutputStream
out.writeObject(instance1); // serialize "instance1" and "instance2" to "out" out.writeObject(instance2); out.close(); System.out.println("Serialized..."); } catch (IOException e) { System.err.println("Something screwed up while serializing"); e.printStackTrace(); System.exit(1); }
// Deserialize try { ObjectInput in = new ObjectInputStream(new FileInputStream("objects.dat")); // open ObjectInputStream
Object readObject1 = in.readObject(); // read two objects from "in" Object readObject2 = in.readObject(); // you may want to cast them to the appropriate types in.close(); System.out.println("Deserialized...");
System.out.println(readObject1); System.out.println(readObject2); } catch (IOException e) { System.err.println("Something screwed up while deserializing"); e.printStackTrace(); System.exit(1); } catch (ClassNotFoundException e) { System.err.println("Unknown class for deserialized object"); e.printStackTrace(); System.exit(1); } }
}</lang>
Objective-C
About Cocoa, I can't test it, but I've used Apple's documentation to learn how to do it (see here; serializing or marshalling is rather known in Obj-C world as archiving).
There exists also a way of serializing without the GNUstep/Cocoa framework, using the runtime of Objective-C (so it could be slightly implementation dependent, see Serialization on Wikipedia). (I will work on it and will put here a full working example compatible with the task).
<lang objc>#import <Foundation/Foundation.h>
// a fantasy two level hierarchy @interface Animal : NSObject <NSCoding> {
NSString *animalName; int numberOfLegs;
} - (id) init; - (id) initWithName: (NSString*)name andLegs: (NSInteger)legs; - (void) dump; // the following allows "(de)archiving" of the object - (void) encodeWithCoder: (NSCoder*)coder; - (id) initWithCoder: (NSCoder*)coder; @end
@implementation Animal - (id) init {
self = [super init]; return self;
} - (id) initWithName: (NSString*)name andLegs: (NSInteger)legs {
self = [super init]; animalName = [name retain]; numberOfLegs = legs; return self;
} - (void) dealloc {
[animalName release]; [super dealloc];
} - (void) dump {
NSLog(@"%@ has %d legs", animalName, numberOfLegs);
} // ======== - (void) encodeWithCoder: (NSCoder*)coder {
[coder encodeObject: animalName forKey: @"Animal.name"]; [coder encodeInt: numberOfLegs forKey: @"Animal.legs"];
} - (id) initWithCoder: (NSCoder*)coder {
self = [super init]; animalName = [[coder decodeObjectForKey: @"Animal.name"] retain]; numberOfLegs = [coder decodeIntForKey: @"Animal.legs"]; return self;
} @end
@interface Mammal : Animal <NSCoding> {
BOOL hasFur; NSMutableArray *eatenList;
} - (id) init; - (id) initWithName: (NSString*)name hasFur: (BOOL)fur; - (void) addEatenThing: (NSString*)thing; - (void) dump; // for archiving / dearchiving: - (void) encodeWithCoder: (NSCoder*)coder; - (id) initWithCoder: (NSCoder*)coder; @end
@implementation Mammal - (id) init {
self = [super init]; hasFur = NO; eatenList = [[NSMutableArray alloc] initWithCapacity: 10]; return self;
} - (id) initWithName: (NSString*)name hasFur: (BOOL)fur {
self = [super initWithName: name andLegs: 4]; hasFur = fur; eatenList = [[NSMutableArray alloc] initWithCapacity: 10]; return self;
} - (void) addEatenThing: (NSString*)thing {
[eatenList addObject: thing];
} - (void) dealloc {
[eatenList release]; [super dealloc];
} - (void) dump {
[super dump]; NSLog(@"has fur? %@", (hasFur) ? @"yes" : @"no" ); // fast enum not implemented yet in gcc 4.3, at least // without a patch that it seems to exist... NSEnumerator *en = [eatenList objectEnumerator]; id element; NSLog(@"it has eaten %d things:", [eatenList count]); while( (element = [en nextObject]) != nil ) NSLog(@"it has eaten a %@", element); NSLog(@"end of eaten things list");
} // ========= de/archiving - (void) encodeWithCoder: (NSCoder*)coder {
[super encodeWithCoder: coder]; [coder encodeBool: numberOfLegs forKey: @"Mammal.hasFur"]; [coder encodeObject: eatenList forKey: @"Mammal.eaten"];
} - (id) initWithCoder: (NSCoder*)coder {
self = [super initWithCoder: coder]; hasFur = [coder decodeBoolForKey: @"Mammal.hasFur"]; eatenList = [[coder decodeObjectForKey: @"Mammal.eaten"] retain]; return self;
} @end
int main()
{
Mammal *aMammal; Animal *anAnimal; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // let us create a fantasy animal anAnimal = [[Animal alloc]
initWithName: @"Eptohippos" andLegs: 7 ];
// for some reason an Eptohippos is not an horse with 7 legs, // and it is not a mammal, of course...
// let us create a fantasy mammal (which is an animal too) aMammal = [[Mammal alloc]
initWithName: @"Mammaluc" hasFur: YES ];
// let us add some eaten stuff... [aMammal addEatenThing: @"lamb"]; [aMammal addEatenThing: @"table"]; [aMammal addEatenThing: @"web page"];
// dump anAnimal NSLog(@"----- original Animal -----"); [anAnimal dump];
// dump aMammal... NSLog(@"----- original Mammal -----"); [aMammal dump];
// now let us store the objects... NSMutableData *data = [[NSMutableData alloc] init]; NSKeyedArchiver *arch = [[NSKeyedArchiver alloc]
initForWritingWithMutableData: data];
[arch encodeObject: anAnimal forKey: @"Eptohippos"]; [arch encodeObject: aMammal forKey: @"Mammaluc"]; [anAnimal release]; [aMammal release]; [arch finishEncoding]; [arch release]; [data writeToFile: @"objects.dat" atomically: YES]; [data release];
// now we want to retrieve the saved objects... NSData *ldata = [[NSData alloc]
initWithContentsOfFile: @"objects.dat"];
NSKeyedUnarchived *darch = [[NSKeyedUnarchiver alloc]
initForReadingWithData: ldata];
Animal *archivedAnimal = [darch decodeObjectForKey: @"Eptohippos"]; Mammal *archivedMammal = [darch decodeObjectForKey: @"Mammaluc"]; [darch finishDecoding]; [ldata release]; [darch release];
// now let's dump/print the objects... NSLog(@"\n"); NSLog(@"----- the archived Animal -----"); [archivedAnimal dump]; NSLog(@"----- the archived Mammal -----"); [archivedMammal dump];
[pool release]; return EXIT_SUCCESS;
}</lang>
OCaml
Objects which contain methods are difficult to serialize because it will want to serialize those methods too, but functions usually cannot be serialized. Instead, here we perform the task on non-object datatypes, with an outside function to print them.
<lang ocaml>type entity = { name : string }
let create_entity () = { name = "Entity" } let print_entity x = print_endline x.name let create_person () = { name = "Cletus" }
let instance1 = create_person () let instance2 = create_entity ()
(* Serialize *) let out_chan = open_out_bin "objects.dat";; output_value out_chan instance1;; output_value out_chan instance2;; close_out out_chan;;
(* Deserialize *) let in_chan = open_in_bin "objects.dat";; let result1 : entity = input_value in_chan;; let result2 : entity = input_value in_chan;; close_in in_chan;;
print_entity result1;; print_entity result2;;</lang>
Perl
<lang perl>{
package Greeting; sub new { my $v = 'Hello world!'; bless \$v, shift; }; sub stringify { ${shift()}; };
}; {
package Son::of::Greeting; use base qw(Greeting); # inherit methods sub new { # overwrite method of super class my $v = 'Hello world from Junior!'; bless \$v, shift; };
}; {
use Storable qw(store retrieve); package main; my $g1 = Greeting->new; my $s1 = Son::of::Greeting->new; print $g1->stringify; print $s1->stringify;
store $g1, 'objects.dat'; my $g2 = retrieve 'objects.dat';
store $s1, 'objects.dat'; my $s2 = retrieve 'objects.dat';
print $g2->stringify; print $s2->stringify;
};</lang>
PHP
Serialization in PHP is straightforward. The built-in function serialize() handles it in a single statement. <lang php>$myObj = new Object(); $serializedObj = serialize($myObj);</lang> In order to un-serialize the object, use the unserialize() function. Note that the class of object must be defined in the script where un-serialization takes place, or the class' methods will be lost.
Python
<lang python># Object Serialization in Python
- serialization in python is accomplished via the Pickle module.
- Alternatively, one can use the cPickle module if speed is the key,
- everything else in this example remains the same.
import pickle
class Entity: def __init__(self): self.name = "Entity" def printName(self): print self.name
class Person(Entity): #OldMan inherits from Entity def __init__(self): #override constructor self.name = "Cletus"
instance1 = Person() instance1.printName()
instance2 = Entity() instance2.printName()
target = file("objects.dat", "w") # open file
- Serialize
pickle.dump((instance1, instance2), target) # serialize `instance1` and `instance2`to `target` target.close() # flush file stream print "Serialized..."
- Unserialize
target = file("objects.dat") # load again i1, i2 = pickle.load(target) print "Unserialized..."
i1.printName() i2.printName()</lang>
Ruby
The core class Marshal
handles object serialization. The dump
method serializes an object, and the load
method reconstitutes it.
<lang ruby>class Being
def initialize(specialty=nil) @specialty=specialty end def to_s "(object_id = #{object_id})\n"+"(#{self.class}):".ljust(12)+to_s4Being+(@specialty ? "\n"+" "*12+@specialty : "") end def to_s4Being "I am a collection of cooperative molecules with a talent for self-preservation." end
end
class Earthling < Being
def to_s4Being "I originate from a blue planet.\n"+" "*12+to_s4Earthling end
end
class Mammal < Earthling
def initialize(type) @type=type end def to_s4Earthling "I am champion in taking care of my offspring and eating everything I can find, except mammals of type #{@type}." end
end
class Fish < Earthling
def initialize(iq) @iq=(iq>1 ? :instrustableValue : iq) end def to_s4Earthling "Although I think I can think, I can't resist biting in hooks." end
end
class Moonling < Being
def to_s4Being "My name is Janneke Maan, and apparently some Earthlings will pay me a visit." end
end
diverseCollection=[] diverseCollection << (marsian=Being.new("I come from Mars and like playing hide and seek.")) diverseCollection << (me=Mammal.new(:human)) diverseCollection << (nemo=Fish.new(0.99)) diverseCollection << (jannakeMaan=Moonling.new)
puts "BEGIN ORIGINAL DIVERSE COLLECTION" diverseCollection.each do |being|
puts "",being.to_s
end puts "END ORIGINAL DIVERSE COLLECTION" puts "\n"+"*"*50+"\n\n"
- Marshal the diverse Array of beings
File.open('diverseCollection.bin','w') do |fo|
fo << Marshal.dump(diverseCollection)
end
- load the Array of diverse beings
sameDiverseCollection=Marshal.load(File.read('diverseCollection.bin'))
puts "BEGIN LOADED DIVERSE COLLECTION" puts(
sameDiverseCollection.collect do |being| being.to_s end.join("\n\n") )
puts "END LOADED DIVERSE COLLECTION"</lang>
Tcl
This example uses an experimental package, available from The Tcler's Wiki. <lang tcl>package require Tcl 8.6 package require TclOO::serializer 0.1
- These classes are inspired by the Perl example
oo::class create Greeting {
superclass oo::serializable variable v constructor {} { set v "Hello world!" } method get {} { return $v }
} oo::class create SubGreeting {
superclass Greeting oo::serializable variable v constructor {} { set v "Hello world from Junior!" }
} oo::class create GreetingsHolder {
superclass oo::serializable variable o1 o2 constructor {greeting1 greeting2} { set o1 $greeting1 set o2 $greeting2 } method printGreetings {} { puts [$o1 get] puts [$o2 get] } destructor { $o1 destroy $o2 destroy }
}
- Make some objects and store them
GreetingsHolder create holder [Greeting new] [SubGreeting new] set f [open "objects.dat" w] puts $f [oo::serialize holder] close $f
- Delete the objects
holder destroy
- Recreate the objects from the file and show that they work
set f [open "objects.dat" r] set obj [oo::deserialize [read $f]] close $f $obj printGreetings</lang>