1

I have to serialize and deserialize in Perl. I am aware that Data::Dumper and eval are not the best suited ones for this job but I am not allowed to modify this aspect in the legacy scripts which I am working on.
Below are two ways ( CODE 1 and CODE 2 ) to use eval.
In CODE 1, the hash is available as a string before being deserialized via eval.
In CODE 2, the hash is serialized using Dumper before being deserialized via eval.

In both the code samples, one of two attempted ways to deserialize works. Why does the other way to deserialize not work ?

CODE 1

my $r2 = "( 
'w' => {
           'k2' => 5,
           'k1' => 'key',
           'k3' => [
                     'a',
                     'b',
                     2,
                     '3'
                   ]
         },
  'q' => 2 
)"; 

my %z; 
eval "\%z = $r2";          ####### Works. 
print "\%z = [".Data::Dumper::Dumper (\%z)."] "; 

my $answer = eval "$r2";   #### Does NOT work. 
print "\n\nEvaled = [".Dumper($answer)."] "; 

Output

%z = [$VAR1 = {
          'w' => {
                   'k2' => 5,
                   'k1' => 'key',
                   'k3' => [
                             'a',
                             'b',
                             2,
                             '3'
                           ]
                 },
          'q' => 2
        };
]

Evaled = [$VAR1 = 2;
]

But below code works in reverse manner :
CODE 2

my %a = ( "q" =>2, "w"=>{ "k1"=>"key", "k2"=>5, k3=>["a", "b", 2, "3",], }, );  **# Same hash as above example.** 
$Data::Dumper::Terse=1; 
$Data::Dumper::Purity = 1; 
my $r2 = Dumper(\%a); 

my %z; 
eval '\%z = $r2'; 
print "\n\n\%z = [".Dumper(\%z)."] ";         #### Does NOT work. 

my $answer = eval $r2; 
print "\n\nEvaled = [".Dumper($answer)."] ";  ####### Works.

Output

%z = [$VAR1 = {};
]

Evaled = [$VAR1 = {
          'w' => {
                   'k2' => 5,
                   'k1' => 'key',
                   'k3' => [
                             'a',
                             'b',
                             2,
                             '3'
                           ]
                 },
          'q' => 2
        };
]
gsinha
  • 1,165
  • 2
  • 18
  • 43

2 Answers2

2

First of all, please don't put comments that result in syntax errors (**).

Notice that the string you provided in the first code block produces different data structure than the Dumper function. In the first block you are creating a hash, but you don't assign it to any variable. In case of the Dumper function, anonymous hash is created and it's reference is passed to the $VAR variable.

To make the first code work, you should replace ( with { to create anonymous hash and then assign it to a variable, for example:

my $r2 = "$VAR = { 
    'w' => {
           'k2' => 5,
           'k1' => 'key',
           'k3' => [
                     'a',
                     'b',
                     2,
                     '3'
                   ]
         },
  'q' => 2 
}"; 
bart
  • 898
  • 4
  • 5
  • Thanks for your time. Currently, i am using pattern match and replace to change the leading and trailing curly braces with round brackets. Could we do it in any other way too ? Also, I have removed the stars. I should have put them behind the #. – gsinha Mar 28 '16 at 15:00
  • You don't need to change all that data. Merely change how you make the assignments. – brian d foy Mar 28 '16 at 19:40
1

Don't use Data::Dumper to serialize data. Having said that...

This is a problem of scalar and list context. In the second eval, you have:

my $answer = ...;

Since you are assigning to a scalar, the right side is evaluated in scalar context. That means the eval is in scalar context. That value is:

(
'w' => {
           'k2' => 5,
           'k1' => 'key',
           'k3' => [
                     'a',
                     'b',
                     2,
                     '3'
                   ]
         },
  'q' => 2
)

That looks like a list, but it's really the comma operator in scalar context. That evaluates the left hand side, discards that result, and returns the righthand side. So, my $x = ( 'left', 'right' ) assigns right to $x. This is covered in What is the difference between a list and an array? in perlfaq4.

In your question, you see that $r gets the value 2. That's the rightmost value in the comma chain, so that's the value you get back in scalar context. Change that to another value (perhaps 'duck'), and that's the value that you'll get back:

my $r2 = "(
    'w' => {
               'k2' => 5,
             },
      'q' => 'duck'
    )";

my $answer = eval "$r2";

use Data::Dumper;
print "Evaled =\n" . Dumper($answer);

It's not a number, which confuses people because they think it's some sort of count:

Evaled =
$VAR1 = 'duck';

Change that to assign in list context (that hash assignment is a list assignment) and you get the right answer:

my $r2 = "(
    'w' => {
               'k2' => 5,
             },
      'q' => 'duck'
    )";

my @answer = eval "$r2";

use Data::Dumper;
print "Evaled =\n" . Dumper(\@answer);

Now it's the data structure you thought it should be:

Evaled =
$VAR1 = [
          'w',
          {
            'k2' => 5
          },
          'q',
          'duck'
        ];
brian d foy
  • 129,424
  • 31
  • 207
  • 592