Search code examples
perlserializationsyntaxdata-retrievalhashref

Syntax to use retrieve() to deserialize a hash?


I'm having trouble understanding how to deserialize a hash with retrieve() in Perl (v5.30.0):

#!/usr/bin/perl
use warnings;
use strict;
use Storable;

package main;

my %origHash = (key1 => 'data1',
        key2 => 'data2',
        key3 => 'data3',
        key4 => 'data4');

# Retrieve, modify, and retrieve again in %origHash:
printf "Retrieved:  %s\n", $origHash{'key2'};
$origHash{'key2'} = 'data22';   # Modify value
$origHash{'key5'} = 'data5';    # Add new value
printf "Retrieved:  %s %s\n", $origHash{'key2'}, $origHash{'key5'};


# Serialize origHash, store in file 'file':
store \%origHash, 'file';

# Deserialize the hash, now store data in ref 'hashref':
my %hashref = retrieve('file');                                    # Line 24

# Retrieve, modify, and retrieve again in 'hashref':
printf "Retrieved:  %s\n", $hashref{'key2'};                       # Line 27
$hashref{'key2'} = 'data222';   # Modify value
$hashref{'key6'} = 'data6';     # Add new value
printf "Retrieved:  %s %s\n", $hashref{'key2'}, $hashref{'key6'};

Output is:

me@ubuntu01$ ./SerHash.perl
Retrieved:  data2
Retrieved:  data22 data5
Reference found where even-sized list expected at ./SerHash.perl line 24.
Use of uninitialized value in printf at ./SerHash.perl line 27.
Retrieved:
Retrieved:  data222 data6
me@ubuntu01$

So the first half of the script is Perl/Hash 101, demonstrating the simple syntax of making, retrieving from, and modifying values in a hash. Then I successfully serialize the hash and store in in file file with this line: store \%origHash, 'file';.

I'm not sure if that worked...? Here's what I see when I look in file after the above store command is executed:

me@ubuntu01$ more file
pst0
   12345678
data4
data1
data3
data5
data22
me@ubuntu01$

Huh. So my values are there, but where are the keys?

Back to the test script: After serializing the hash, I try to deserialize the hash from file in this line: my %hashref = retrieve('file'); ...although I'm not totally certain about that syntax. Note that if I try to retrieve data from the deserialized hash with this command...

printf "Retrieved:  %s\n", $hashref{'key2'};                       # Line 27

...the target value appears to be uninitialized? Here's the output from that command:

Use of uninitialized value in printf at ./SerHash.perl line 27.
Retrieved:

Ugh, so that's not great. I note that I can add new values into the deserialized hash, but that's kinda beside the point if the data from original hash isn't available.

This test code was built using this tutorial, which left me with a shakey understanding of the retrieve() function. According to the documentation:

To retrieve data stored to disk, use retrieve with a file name. The objects stored into that file are recreated into memory for you, and a reference to the root object is returned. In case an I/O error occurs while reading, undef is returned instead.

So retrieve() returns a reference to the root object[.]? Hmmm... So shouldn't the code be:

# Deserialize the hash, now store data in ref 'hashref':
# (***NOTE*** that we define hashref with a '$', not '%' now!)
my $hashref = retrieve('file');                                    # Line 24

# Retrieve, modify, and retrieve again in 'hashref':
printf "Retrieved:  %s\n", $hashref{'key2'};                       # Line 27
$hashref{'key2'} = 'data222';   # Modify value                     # Line 28
$hashref{'key6'} = 'data6';     # Add new value                    # Line 29
printf "Retrieved:  %s %s\n", $hashref{'key2'}, $hashref{'key6'};  # Line 30

Now output is:

me@ubuntu01$ ./SerHash.perl
Global symbol "%hashref" requires explicit package name (did you forget to declare "my %hashref"?) at ./SerHash.perl line 27.
Global symbol "%hashref" requires explicit package name (did you forget to declare "my %hashref"?) at ./SerHash.perl line 28.
Global symbol "%hashref" requires explicit package name (did you forget to declare "my %hashref"?) at ./SerHash.perl line 29.
Global symbol "%hashref" requires explicit package name (did you forget to declare "my %hashref"?) at ./SerHash.perl line 30.
Global symbol "%hashref" requires explicit package name (did you forget to declare "my %hashref"?) at ./SerHash.perl line 30.
me@ubuntu01$

Gah! This looks even worse. I don't understand what is being returned by retrieve() nor how to handle it syntactically. Can anyone help?


Solution

  • Since retrieve returns a reference, you do want to use a scalar ($hashref), as in your 2nd attempt. The problem is that you are trying to use $hashref as a hash. You need to use dereferencing syntax: $hashref->{key2}:

    my $hashref = retrieve('file');                                    # Line 24
    
    # Retrieve, modify, and retrieve again in 'hashref':
    printf "Retrieved:  %s\n", $hashref->{'key2'};                       # Line 27
    $hashref->{'key2'} = 'data222';   # Modify value
    $hashref->{'key6'} = 'data6';     # Add new value
    printf "Retrieved:  %s %s\n", $hashref->{'key2'}, $hashref->{'key6'};