Search code examples
perlmoosemoo

Unblessing Perl objects and constructing the TO_JSON method for convert_blessed


In this answer I found a recommendation for a simple TO_JSON method, which is needed for serializing blessed objects to JSON.

sub TO_JSON { return { %{ shift() } }; }

Could anybody please explain in detail how it works?

I changed it to:

sub TO_JSON {
        my $self = shift;         # the object itself – blessed ref
        print STDERR Dumper $self;

        my %h = %{ $self };       # Somehow unblesses $self. WHY???
        print STDERR Dumper \%h;  # same as $self, only unblessed

        return { %h };    # Returns a hashref that contains a hash.
        #return \%h;      # Why not this? Works too…
}

Many questions… :( Simply, I’m unable to understand 3-liner Perl code. ;(

I need the TO_JSON but it will filter out:

  • unwanted attributes and
  • unset attributes too (e.g. for those the has_${attr} predicate returns false)

This is my code – it works but I really don't understand why the unblessing works…

use 5.010;
use warnings;
use Data::Dumper;

package Some;
use Moo;

has $_ => ( is => 'rw', predicate => 1,) for (qw(a1 a2 nn xx));

sub TO_JSON {
    my $self = shift;
    my $href;
    $href->{$_} = $self->$_ for( grep {!/xx/} keys %$self );
    # Same mysterious unblessing. The `keys` automagically filters out
    # “unset” attributes without the need of call of the has_${attr}
    # predicate… WHY?
    return $href;
}

package main;
use JSON;
use Data::Dumper;

my @objs = map { Some->new(a1 => "a1-$_", a2 => "a2-$_", xx=>"xx-$_") } (1..2);
my $data = {arr => \@objs};
#say Dumper $data;
say JSON->new->allow_blessed->convert_blessed->utf8->pretty->encode($data);

EDIT: To clarify the questions:

  • The %{ $hRef } derefences the $hRef (getting the hash pointed to by the reference), but why get a plain hash from a blessed object reference $self?
  • In other words, why the $self is a hashref?
  • I tried to make a hash slice like @{$self}{ grep {!/xx/} keys %$self} but it didn't work. Therefore I created that horrible TO_JSON.
  • If the $self is a hashref, why the keys %$self returns only attributes having a value, and not all declared attributes (e.g. the nn too – see the has)?

Solution

  • sub TO_JSON { return { %{ shift() } }; }
                         | |  |
                         | |  L_ 1. pull first parameter from `@_`
                         | |        (hashref/blessed or not)
                         | |     
                         | L____ 2. dereference hash (returns key/value list)
                         |
                         L______ 3. return hashref assembled out of list
    

    In your TO_JSON() function { %h } returns a shallow hash copy, while \%h returns a reference to %h (no copying).