How can I unbless an object in Perl?
From perldoc -f bless:
开发者_运维问答
bless REF,CLASSNAME
This function tells the thingy referenced by
an object in theREF
that it is nowCLASSNAME
package.
Is there any way of obtaining an unblessed structure without unnecessary copying?
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
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 );
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";
Acme::Curse :)
Update: Thank you, Ivan! I mixed up modules. Actually I wanted to give a link to Acme::Damn :))
P. S. See also Acme::Sneeze :)
P. P. S. It has no real use, that's why it's Acme::
. See brian's post.
精彩评论