Strange behavior of a tied hash in perl, when asking for an arrayref
Asked Answered
K

3

5

I was trying to tie an hash (or hashref) in order of tracking variable usages.

Everything is working for simple cases, but when I tried to use my module on some real code I had this error:

hash- or arrayref expected (not a simple scalar, use allow_nonref to allow this)

I've replicated the error using the following code:

use Tie::Hash::Usages;
use JSON;

my @arr = (
    {
        key1 => "ac",
        key2 => 12,
        key3 => 12
    },        
);
my %tied_hash;


tie %tied_hash, 'Tie::Hash::Usages';

$tied_hash{key1} = \@arr;

my @val = $tied_hash{key1};
print encode_json(\@val)."\n\n"; #this works

print encode_json($tied_hash{key1}); #this doesn't

The same code works with a plain hash.

I'd need this to work also in the second case, the code base is huge and I don't want to change it or live with the doubt that something somewhere will not work in some particular case.

Usages.pm (simplified)

package Tie::Hash::Usages;
use strict;
use warnings;

use Tie::Hash;

use vars qw(@ISA);

@ISA = qw(Tie::StdHash);

sub TIEHASH {

    my ($class, $tracker, $filename) = @_;
    my %hash;

    bless \%hash, $class;

}

sub STORE {
    my ($self, $key, $val) = @_;
    $self->{$key} = $val;
}

sub DELETE {
    my ($self, $key) = @_;
    delete $self->{$key};

}

sub FETCH {
    my ($self, $key) = @_;
    return $self->{$key};
}

sub DESTROY {
    my $self = shift;
}
1;

perl version: v5.18.2

Kentiga answered 16/11, 2015 at 15:26 Comment(3)
It works for me. I'm getting the error when I try encode_json(@val).Flyaway
Uh really? Yes you get the error in that case because encode_json expects an arrayref as input. What version of perl are you on? I'm on v5.18.2Kentiga
Works for me in 5.8.3 and 5.22.0.Flyaway
C
6

Minimal demonstration:

use JSON::XS  qw( encode_json );
use Tie::Hash qw( );

our @ISA = 'Tie::StdHash';

{
   tie my %tied, __PACKAGE__;
   $tied{data} = { a => 1 };
   encode_json($tied{data});  # Exception: hash- or arrayref expected ...
}

JSON is a front-end for JSON::PP (default) or JSON::XS (if found). This is a problem with JSON::XS.

A lot of XS code doesn't handle magical variables (which is what $tied{EXPR} returns), and while JSON::XS has handled magical values since version 1.2, it doesn't for the value directly passed to encode_json.

This is an existing bug in JSON::XS that can be worked around as follows:

encode_json(my $non_magical = $tied{data})

Bug reported.

Changeover answered 16/11, 2015 at 16:52 Comment(0)
M
1

Unable to replicate using the code given, so what you're providing doesn't seem to be representative of your actual situation. The only thing I see that's the tiniest bit off is this line:

my @val = $tied_hash{key1};

in which you're assigning a scalar (your stored arrayref) to an array. Perl handles this fine, assembling an array with the scalar as sole content, but if your actual use case involves something more complex (maybe something with sub prototypes involved), conceivably something might be going wrong there.

Meld answered 16/11, 2015 at 15:59 Comment(2)
tried again putting everything in a file. But error still happens to me. Weird!Kentiga
@Meld if you don't have JSON::XS installed, JSON.pm will use JSON::PP instead, which uses pure-perl code and will work around the issue.Glyptodont
S
0

Ether got it right. JSON library uses JSON:XS by default (which creates this issue). All I had to do is uninstall JSON::XS and install JSON::PP

  1. sudo cpan
  2. install cpan App::cpanminus
  3. exit
  4. sudo cpanm --uninstall JSON::XS
  5. sudo cpan
  6. install JSON::PP
  7. exit

Hope this helps someone.

Sheff answered 27/6, 2018 at 6:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.