Thursday 12 November 2015

Typed Serialization in Perl

After writing this post about deserialization of a type known at runtime, I remembered how out of the box it is achieving the same in perl.

If you've ever written some perl code you'll probably know about Data::Dumper and eval. So well, basically that's all you need for serializing an object and then deserializing it back without having to pass the type as parameter or anything of the sorts. Let's see.

When Dumper serializes a type, it will write the data in the object (your blessed reference or whatever) to a "perl code string", and furthermore, it will wrap it in a call to bless, passing it the type of the object!. I mean:

$VAR1 = bless( {
                 'places' => {
                               'Asturies' => 'Uvieu',
                               'France' => 'Toulouse',
                               'Germany' => 'Berlin'
                             },
                 'name' => 'Francois',
                 'lastName' => undef,
                 'city' => bless( {
                                    'name' => 'Paris'
                                  }, 'City' )
               }, 'Person' );

So we have there perfectly valid code to create a perl object of the right type with the right data. Obviously to dinamically run a string of perl code in our script all we need is calling the almighty eval. Well, just one minor quirk. We have to get rid of the $VAR1 = assignment. Oddly enough perl does not have replace function out of the box to do something like replace(mySerializedObject, '$VAR1 = ', ''), so you either use a regex, or substr

I've put the few lines of code needed for this serialization/deserialization into a class, in case one day I need to change the serialization strategy.

package TypedSerializer;

use strict;
#Data::Dumper::Dump stringifies the content of the object and wraps it in a call to bless with the corresponding type, 
#the main problem is that it adds this assignment " $VAR1 = bless(" ,
#if we pass this string directly to eval, it will fail because of that "$VAR1 = ", so we just need to remove "$VAR1 = " from the string
#so we either use Terse to directly prevent it from going to the string, or we just remove it with substr or a regex

use Data::Dumper;

sub New{
 my ($class) = @_;           
    my $self = {
   };               
   return bless $self, $class;   
}

sub Serialize{
 my ($self, $item) = @_;
 my $str = Dumper($item);
 return substr($str, length('$VAR1 = '));
}

#bear in mind that when the object is deserialized, the "constructor", New, is not called
sub Deserialize{
 my ($self, $str) = @_;
 return eval $str;
}
1;

I've put it up in a gist along with some testing code here

No comments:

Post a Comment