从 Moose class 获取所有属性作为散列的更好方法
Better way to get all attributes from a Moose class as a hash
我想从 class 中获取所有属性作为散列。
有比这更好的方法吗?
理想情况下(?)我希望能够说出类似的话:
my $hash = \%{ Diag->new( {range =>1, code => 'AB'} ) };
但会满足于:
my $d = Diag->new( {range =>1, code => 'AB'} );
my $hash = $d->hash;
package Diag;
use Moose;
my @attrs = qw/range code severity source message/;
has 'range' => ( is => 'rw', isa => 'Int' );
has 'code' => ( is => 'rw', isa => 'String' );
has 'severity' => ( is => 'rw', isa => 'Int' );
has 'source' => ( is => 'rw', isa => 'String' );
has 'message' => ( is => 'rw', isa => 'String' );
sub hash {
my $self = shift;
my $hash = {};
for (@attrs) {
$hash->{$_} = $self->$_;
}
return $hash;
}
no Moose;
1;
EDIT pack/unpack:
的字符串输出散列
# Combining this attribute and the record_format would be great.
# if $self->record->format worked that would be cool.
has 'record' => (
is => 'ro',
isa => 'HashRef',
default => sub {
{
foo => 'A5',
foo2 => 'A16',
}
);
sub record_format
{
my $self = shift;
my @fields = qw( foo foo2 );
return _build_format_string($self->record, \@fields);
}
sub _build_format_string {
return join '', map { $_[1]->{$_} } @{ $_[2] };
}
EDIT2
我发现如果我创建一个属性特征,我可以让它变得更好一点。这样hash order是和属性一起的,只需要一种格式化方法。
package Order;
use Moose::Role;
has order => (
is => 'ro',
isa => 'ArrayRef',
predicate => 'has_order',
);
Moose::Util::meta_attribute_alias('Order');
1;
package Record;
use Moose;
has 'record' => (
traits => [qw/Order/],
is => 'ro',
isa => 'HashRef',
default => sub {
{
foo => 'A5',
foo2 => 'A16',
},
;
},
order => [qw(foo foo2)]
);
sub format {
my ( $self, $attr ) = @_;
my $fields = $self->meta->get_attribute($attr)->order();
return join '', map { $self->{$attr}{$_} } @$fields;
}
1;
my $r = Record->new();
print $r->format("record");
Outputs: A5A16
我更愿意将它打包到一个方法中,但是你的 "ideal" 案例几乎就在那里了
my $data = { %{ Diag->new( {range =>1, code => 'AB'} ) } };
%{...}
return 是一个 (key,value,...) 列表,因此您希望 {}
生成哈希引用的,而不是 \
(奇怪的是它又变成了一个对象)。
但实际上,这应该隐藏在一个方法中
my $data = Diag->new(...)->get_data;
package Diag;
...
sub get_data { return { %{$_[0]} } };
...
1;
出于纯粹的展示目的——将它们打印出来——考虑使用模块,这样您就不必担心(或知道)哪些属性具有哪些引用作为值。我使用 Data::Dump 是为了使其输出简洁
my $obj = Diag->new(...);
say $obj->stringify(); # whole object serialized
say for $obj->stringify('attr1', 'attr1', ...); # serialized values for each
package Diag;
...
use Data::Dump qw(pp);
...
sub stringify {
my $self = shift;
return map { pp $self->{$_} } @_ if @_;
return { pp %$self } }
}
如果使用原生 OO 而不是 Moo
/Moose
也会为 say $obj;
重载 ""
使用
use overload q("") => sub { return shift->stringify() }
在 Moo
和 Moose
中提供了 ""
下对象的字符串化(也暗示在打印中)。
进一步说明,以下代码并未解决实际问题。我将进行编辑,但我暂时保留它,因为它被认为通常有用。
评论和问题编辑中提到,部分意图是能够检索属性值并打包。添加的代码会执行此操作,但由于存在显式取消引用,因此应添加对 ref
的检查,以便从 arrayref、hashref 或 string/number 中正确检索所有值。例如
sub record_format {
my ($self, @attrs) = @_;
@attrs = qw(attr1 attr2 ...) if not @attrs; # default list
my $packed;
foreach my $attr (@attrs) {
my $val = $self->{$attr};
my $rv = ref $val;
if (not $rv) { $packed .= $val }
elsif ($rv eq 'HASH') { $packed .= join '', values %$val }
elsif ($rv eq 'ARRAY') { $packed .= join '', @$val }
}
return $packed;
}
这会打包传递的属性值或列出的默认值。
所需的 $self->record->format
无法很好地工作,因为 $self->record
不是 return 对象,因此您无法串接另一个方法调用。您 可以 编写一个访问器,但是如果您在任何情况下将其 return 设为一个对象,这可能会是一个令人惊讶的行为,因此不是好的设计。
我想从 class 中获取所有属性作为散列。 有比这更好的方法吗? 理想情况下(?)我希望能够说出类似的话:
my $hash = \%{ Diag->new( {range =>1, code => 'AB'} ) };
但会满足于:
my $d = Diag->new( {range =>1, code => 'AB'} );
my $hash = $d->hash;
package Diag;
use Moose;
my @attrs = qw/range code severity source message/;
has 'range' => ( is => 'rw', isa => 'Int' );
has 'code' => ( is => 'rw', isa => 'String' );
has 'severity' => ( is => 'rw', isa => 'Int' );
has 'source' => ( is => 'rw', isa => 'String' );
has 'message' => ( is => 'rw', isa => 'String' );
sub hash {
my $self = shift;
my $hash = {};
for (@attrs) {
$hash->{$_} = $self->$_;
}
return $hash;
}
no Moose;
1;
EDIT pack/unpack:
的字符串输出散列# Combining this attribute and the record_format would be great.
# if $self->record->format worked that would be cool.
has 'record' => (
is => 'ro',
isa => 'HashRef',
default => sub {
{
foo => 'A5',
foo2 => 'A16',
}
);
sub record_format
{
my $self = shift;
my @fields = qw( foo foo2 );
return _build_format_string($self->record, \@fields);
}
sub _build_format_string {
return join '', map { $_[1]->{$_} } @{ $_[2] };
}
EDIT2 我发现如果我创建一个属性特征,我可以让它变得更好一点。这样hash order是和属性一起的,只需要一种格式化方法。
package Order;
use Moose::Role;
has order => (
is => 'ro',
isa => 'ArrayRef',
predicate => 'has_order',
);
Moose::Util::meta_attribute_alias('Order');
1;
package Record;
use Moose;
has 'record' => (
traits => [qw/Order/],
is => 'ro',
isa => 'HashRef',
default => sub {
{
foo => 'A5',
foo2 => 'A16',
},
;
},
order => [qw(foo foo2)]
);
sub format {
my ( $self, $attr ) = @_;
my $fields = $self->meta->get_attribute($attr)->order();
return join '', map { $self->{$attr}{$_} } @$fields;
}
1;
my $r = Record->new();
print $r->format("record");
Outputs: A5A16
我更愿意将它打包到一个方法中,但是你的 "ideal" 案例几乎就在那里了
my $data = { %{ Diag->new( {range =>1, code => 'AB'} ) } };
%{...}
return 是一个 (key,value,...) 列表,因此您希望 {}
生成哈希引用的,而不是 \
(奇怪的是它又变成了一个对象)。
但实际上,这应该隐藏在一个方法中
my $data = Diag->new(...)->get_data;
package Diag;
...
sub get_data { return { %{$_[0]} } };
...
1;
出于纯粹的展示目的——将它们打印出来——考虑使用模块,这样您就不必担心(或知道)哪些属性具有哪些引用作为值。我使用 Data::Dump 是为了使其输出简洁
my $obj = Diag->new(...);
say $obj->stringify(); # whole object serialized
say for $obj->stringify('attr1', 'attr1', ...); # serialized values for each
package Diag;
...
use Data::Dump qw(pp);
...
sub stringify {
my $self = shift;
return map { pp $self->{$_} } @_ if @_;
return { pp %$self } }
}
如果使用原生 OO 而不是 Moo
/Moose
也会为 say $obj;
重载 ""
使用
use overload q("") => sub { return shift->stringify() }
在 Moo
和 Moose
中提供了 ""
下对象的字符串化(也暗示在打印中)。
进一步说明,以下代码并未解决实际问题。我将进行编辑,但我暂时保留它,因为它被认为通常有用。
评论和问题编辑中提到,部分意图是能够检索属性值并打包。添加的代码会执行此操作,但由于存在显式取消引用,因此应添加对 ref
的检查,以便从 arrayref、hashref 或 string/number 中正确检索所有值。例如
sub record_format {
my ($self, @attrs) = @_;
@attrs = qw(attr1 attr2 ...) if not @attrs; # default list
my $packed;
foreach my $attr (@attrs) {
my $val = $self->{$attr};
my $rv = ref $val;
if (not $rv) { $packed .= $val }
elsif ($rv eq 'HASH') { $packed .= join '', values %$val }
elsif ($rv eq 'ARRAY') { $packed .= join '', @$val }
}
return $packed;
}
这会打包传递的属性值或列出的默认值。
所需的 $self->record->format
无法很好地工作,因为 $self->record
不是 return 对象,因此您无法串接另一个方法调用。您 可以 编写一个访问器,但是如果您在任何情况下将其 return 设为一个对象,这可能会是一个令人惊讶的行为,因此不是好的设计。