package Data::MultiValued::UglySerializationHelperRole; use Moose::Role; use namespace::autoclean; # ABSTRACT: only use this if you know what you're doing =head1 SYNOPSIS package My::Class; use Moose; use Data::MultiValued::AttributeTrait::Tags; with 'Data::MultiValued::UglySerializationHelperRole'; has tt => ( is => 'rw', isa => 'Int', traits => ['MultiValued::Tags'], default => 3, predicate => 'has_tt', clearer => 'clear_tt', ); Later: my $json = JSON::XS->new->utf8; my $obj = My::Class->new(rr=>'foo'); my $str = $json->encode($obj->as_hash); my $obj2 = My::Class->new_in_place($json->decode($str)); # $obj and $obj2 have the same contents =head1 DESCRIPTION This is an ugly hack. It pokes inside the internals of your objects, and will break if you're not careful. It assumes that your instances are hashref-based. It mostly assumes that you're not storing blessed refs inside the multi-value attributes. It goes to these lengths to give a decent serialisation performance, without lots of unnecessary copies. Oh, and on de-serialise it will skip all type constraint checking and bypass the accessors, so it may well give you an unusable object. =method C my $obj = My::Class->new_in_place($hashref); Directly Ces the hashref into the class, then calls C<_rebless_slot> on any multi-value attribute. This is very dangerous, don't try this at home without the supervision of an adult. =cut sub new_in_place { my ($class,$hash) = @_; my $self = bless $hash,$class; for my $attr ($class->meta->get_all_attributes) { if ($attr->does('Data::MultiValued::AttributeTrait')) { $attr->_rebless_slot($self); } } return $self; } =method C my $hashref = $obj->as_hash; Performs a shallow copy of the object's hash, then replaces the values of all the multi-value slots with the results of calling C<_as_hash> on the corresponding multi-value attribute. This is very dangerous, don't try this at home without the supervision of an adult. =cut sub as_hash { my ($self) = @_; my %ret = %$self; for my $attr ($self->meta->get_all_attributes) { if ($attr->does('Data::MultiValued::AttributeTrait')) { my $st = $attr->_as_hash($self); if ($st) { $ret{$attr->full_storage_slot} = $st; } else { delete $ret{$attr->full_storage_slot}; } } } return \%ret; } =head1 FINAL WARNING my $obj_clone = My::Class->new_in_place($obj->as_hash); This will create a shallow clone. Most internals will be shared. Things may break. Just don't do it, C the hashref, or do something equivalent (as in the synopsis), instead. =cut 1;