How can I unbless an object in Perl?

From perldoc -f bless:

bless REF,CLASSNAME

This function tells the thingy referenced by REF that it is now
an object in the CLASSNAME package.

Is there any way of obtaining an unblessed structure without unnecessary copying?


Solution 1:

Data::Structure::Util

unbless($ref)

Remove the blessing from any objects found within the passed data structure.

#!/usr/bin/perl

use strict; use warnings;

use Scalar::Util qw( refaddr );
use Data::Structure::Util qw( unbless );

my $x = bless { a => 1, b => 2 } => 'My';

printf "%s : %s\n", ref $x, refaddr $x;

unbless $x;

printf "%s : %s\n", ref $x, refaddr $x;

Output:

My : 237356
HASH : 237356

Solution 2:

Data::Structure::Util has an unbless function that will do it for you. As Erik points out, JSON::XS won't normally accept blessed references (although I wish it would just ignore that and deal with the data structure). There's no way around it in this case.

But consider why do you think you need to unbless it. Are you doing this for one of your own classes or a different class? This sounds suspiciously like The Wrong Thing To Do. There might be a better way.

You have the same problem as breaking encapsulation because you have to assume that you know what the internal structure of the reference is. If you are going to do that, you can just ignore the object-oriented stuff and access the structure directly.

If you are going to do this for your own class, consider providing a method to return a data structure (which doesn't have to be the original structure) instead of changing the object.

You mention in a follow-up comment that you might be doing this to get around some Template Toolkit behavior. I had this situation in two ways depending on the situation:

  • Only pass the data you need to the template instead of the whole object.
  • Add methods to the object to get the data you want in the template.

Perl is DWIM, but TT is even DWIMmier, which is unfortunate sometimes.


Here's a quick hack where I define a TO_JSON in UNIVERSAL so it applies to all objects. It makes a deep copy, unblesses it, and returns the data structure.

#!perl
use v5.10;

sub UNIVERSAL::TO_JSON {
    my( $self ) = shift;

    use Storable qw(dclone);
    use Data::Structure::Util qw(unbless);

    my $clone = unbless( dclone( $self ) );

    $clone;
    }

my $data = bless {
    foo => bless( [], 'Local::Array' ),
    quack => bless( {
        map { $_ => bless [$_, $_**2], 'Local::Array' } 
            grep { is_prime } 1 .. 10
        }, 'Local::Hash' ),
    }, 'Local::Hash';

use JSON::XS;
my $jsonner = JSON::XS->new->pretty->convert_blessed(1);
say $jsonner->encode( $data );

Solution 3:

If you know what your object is backed by, you could do this without using packages.

Hash

$obj = bless {}, 'Obj';
print ref $obj, "\n";
$obj = { %$obj };
print ref $obj, "\n";

Array

$obj = bless [], 'Obj';
print ref $obj , "\n";
$obj = [ @$obj ];
print ref $obj, "\n";

Scalar

$obj = bless \$a, "Obj";
print ref $obj, "\n";
$obj = \${ $$obj };
print ref $obj, "\n";