Unblessing объекты Perl и создание метода TO_JSON для convert_blessed
В этом ответе я нашел рекомендацию для простого TO_JSON
метод, который необходим для сериализации благословенных объектов в JSON.
sub TO_JSON { return { %{ shift() } }; }
Кто-нибудь может объяснить, пожалуйста, как это работает?
Я изменил это на:
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…
}
Много вопросов…:(Просто я не могу понять 3-х строчный код Perl.;(
мне нужно TO_JSON
но это отфильтрует:
- нежелательные атрибуты и
- неустановленные атрибуты тоже (например, для тех, кто
has_${attr}
предикат возвращает false)
Это мой код - он работает, но я действительно не понимаю, почему работает безоблачный...
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);
РЕДАКТИРОВАТЬ: Чтобы уточнить вопросы:
-
%{ $hRef }
разыменовывает$hRef
(получение хеша, на который указывает ссылка), но зачем получать простой хеш из ссылки на благословенный объект$self
? - Другими словами, почему
$self
такое хешреф? -
Я пытался сделать ломтик хеша, как@{$self}{ grep {!/xx/} keys %$self}
но это не сработало.Поэтому я создал это ужасноеTO_JSON
, - Если
$self
это хэшреф, почемуkeys %$self
возвращает только атрибуты, имеющие значение, а не все объявленные атрибуты (например,nn
тоже - посмотриhas
)?
2 ответа
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
В вашем TO_JSON()
функция { %h }
возвращает мелкую копию хэша, в то время как \%h
возвращает ссылку на %h
(без копирования).
Perl реализовал объектную ориентацию, просто сделав так, чтобы ссылка могла знать, из какого пакета она пришла (с bless
). Зная, что ссылка пришла от Foo
пакет означает, что методы действительно являются функциями, определенными в этом пакете.
Perl позволяет получить любую ссылку; не просто ссылки на хеш. Это очень часто - благословлять ссылки на хэш; много документации показывает, что именно это и делается; а также Moose
Является ли; но можно благословить ссылку на массив, ссылку на подпрограмму, дескриптор файла или ссылку на скаляр. Синтаксис %{$self}
работает только с хеш-ссылками (благословенными или нет). Он берет ссылку на хеш и разыменовывает ее как хэш. Тот факт, что первоначальная ссылка, возможно, была благословлена, теряется.
Мне нужен TO_JSON, но что будет отфильтровываться:
- нежелательные атрибуты
- и неустановленные атрибуты (например, для предиката has_${attr} возвращает false.
До 5.20 срезы хеша дают только значения, а не ключи из исходного хеша. Вы хотите и ключи и значения.
Предполагая, что у вас есть хэш, и вы хотите отфильтровать undef
Значения и ключи не входят в белый список, есть несколько вариантов. Вот что у меня есть, используя JSON
модуль:
use strict; # well, I used "use v5.18", but I don't know which version of Perl you're using
use warnings;
use JSON;
my $foo = { foo => undef, bar => 'baz', quux => 5 };
my %whitelist = map { $_, 1 } qw{foo bar};
my %bar = map { $_ => $foo->{$_} }
grep { defined $foo->{$_} && exists $whitelist{$_} }
keys %$foo;
print to_json(\%bar) . "\n";
# well, I used say() instead of print(), but I don't know which version of Perl you're using
map
с и grep
Это не обязательно красиво, но это самый простой способ отфильтровать ключи, не входящие в белый список, и элементы без undef
значение.
Вы можете использовать фрагмент массива:
use strict;
use warnings;
use JSON;
my $foo = { foo => undef, bar => 'baz', quux => 5 };
my @whitelist = qw{foo bar};
my %filtered_on_keys;
@filtered_on_keys{@whitelist} = @$foo{@whitelist};
my %bar = map { $_ => $filtered_on_keys{$_} }
grep { defined $filtered_on_keys{$_} }
keys %filtered_on_keys;
print to_json(\%bar) . "\n";
Или если вы любите петли:
use strict;
use warnings;
use JSON;
my $foo = { foo => undef, bar => 'baz', quux => 5 };
my %whitelist = map { $_ => 1 } qw{foo bar};
my %bar;
while (my ($key, $value) = each %$foo) {
if (defined $value && exists $whitelist{$key}) {
$bar{$key} = $value;
}
}
print to_json(\%bar) . "\n";
Кажется, пришло время вспомнить цитату Ларри Уолла: "Perl разработан, чтобы дать вам несколько способов сделать что-нибудь, поэтому подумайте о выборе наиболее читабельного".
Тем не менее, я подчеркнул, что не все объекты являются хешами. Подходящий способ получить данные от объекта через его функции получения:
use strict;
use warnings;
use JSON;
my $foo = Foo->new({ foo => undef, bar => 'baz', quux => 5 }); # as an example
my %filtered_on_keys;
@filtered_on_keys{qw{foo bar}} = ($foo->get_foo(), $foo->get_bar());
my %bar = map { $_ => $filtered_on_keys{$_} }
grep { defined $filtered_on_keys{$_} }
keys %filtered_on_keys;
print to_json(\%bar) . "\n";