Note 下面有两种序列化方法——使用Storable
(考虑到它需要在不同的 Perl 和模块版本之间,可能会出现问题),以及自定义版本
这是该问题的更一般情况上一个问题 https://stackoverflow.com/q/61053765/4653379,现在要在程序之间传递对象。这一变化带来了实质性的差异。
一个对象必须是连载的 https://en.wikipedia.org/wiki/Serialization为了传递,这样我们就可以通过某种管道逐字节传递它。我用Storable https://perldoc.perl.org/Storable.html为此目的,在此演示中,但您可能需要寻找其他工具或者编写自定义流程(在最后添加的部分展示)
以下是文件,还有一些其他调整(将在下面讨论)。
套餐SharedBetweenPerls.pm
package SharedBetweenPerls;
use warnings;
use strict;
sub new {
my ($class, %args) = @_;
my $self = { %args };
return bless $self, $class;
}
sub get_roleId {
my ($self) = @_;
return $self->{'roleId'};
}
1;
v5.24下需要运行的程序(v.5.24.pl
)
use warnings;
use strict;
use Storable qw(retrieve);
use v5.24;
use FindBin qw($RealBin);
use lib $RealBin; # look for modules in this script's directory
use SharedBetweenPerls;
my ($file) = @ARGV;
my $obj = retrieve($file) // warn "There were errors: $!";
print $obj->get_roleId;
“主”程序,必须在较旧的 Perl 下运行
use warnings;
use strict;
use feature 'say';
use Storable qw(store);
use FindBin qw($RealBin);
use lib $RealBin;
use SharedBetweenPerls;
my $obj = SharedBetweenPerls->new(roleId => 17, username => 'test123');
my $roleId = $obj->get_roleId();
say "Value for 'roleId' in the new object: $roleId";
my $outfile = "obj_$$.storable"; # store the serialized object in a file
store($obj, $outfile) // warn "There were errors: $!"; #/
# (replace my perlbrew path with your actual path to the v5.24 executable)
my $perl_524 = qq($ENV{HOME}/perl5/perlbrew/perls/perl-5.30.0/bin/perl);
my $cmd = qq($perl_524 v5.24.pl $outfile);
my $from_524 = qx( $cmd );
chomp $from_524;
say "Return from function: $from_524";
unlink $outfile or warn "Can't unlink $outfile: $!"; # can delete file now
写入序列化对象的文件的名称应该比我在本演示中使用的名称好得多,并且文件::临时文件 https://perldoc.perl.org/File/Temp.html是处理临时名称的标准选择。
这打印
Value for 'roleId' in the new object: 17
Return from function: 17
因此,对于这个简单的玩具类来说,这是可行的——对象被正确传递。
然而,连载绝不是一件小事。根据您的实际类的复杂程度,特别是两个程序的模块版本的差异程度,可能会出现问题。对于 v5.6 和 v5.24 的组合,我认为您需要祈祷。 (它适用于我的 v5.16 和 v5.30,但 v5.6 非常非常旧。)
组合store
+retrieve
是使用文件传递复杂数据的(一种方法)。我也尝试过freeze
对象并手动将其写入文件,然后读取该文件并thaw
它,而且也有效。 (将冷冻物体直接通过管道传递会很麻烦。)
但是传递整个对象可能不起作用,如果您的情况确实存在问题,那么该怎么做将完全取决于您的类的实际情况。
One thing you can always do is to come up with a custom serialization approach, whereby needed data is passed along (via a file or suitably serialized for a pipe), not the whole object. Then the program on the other end can use that to construct the object.†
如果使用文件传递数据,什么是明确的选项,那么我们正在讨论坚持 https://en.wikipedia.org/wiki/Persistence_(computer_science).
Comments
-
当包定义类时,没有理由导出符号
-
不要硬编码包名称;有__PACKAGE__
为了那个原因。但是对于一个类,包名称作为构造函数中的第一个参数传递,并且应该使用它
-
不要将对象用作任何旧的哈希引用,在这种情况下,只需取消引用键即可打印值。这会触及类的内部结构,并且是一个非常非常糟糕的主意——使用提供的方法。 (为此v.5.24.pl
程序也需要加载带有类的包)
-
如果你希望被调用的程序能够使用一个对象,它应该加载定义该类的包(因为不应该将该对象用作纯粹的哈希引用)
-
The 间接方法表示法 https://perldoc.perl.org/perlobj.html#Invoking-Class-Methods (new ClassName
)非常值得避免。请改用普通方法调用(ClassName->new
)。一方面,构造函数is一个方法
-
程序的参数位于@ARGV
,不在@_
-
上面的类需要更多,但这会带我们去别的地方
-
我建议使用模块来运行外部命令,而不是反引号(qx
)。尝试一些:IPC::System::Simple
, Capture::Tiny
, IPC::Run3
, IPC::Run
† An example
向您的类添加一个方法来实现所需的反/序列化。它可以简单地创建一个包含所有属性及其值的哈希,然后将其序列化——例如,从中生成一个 JSON 字符串。 (如果某些属性的值是其他类的对象,那么您必须做更多的工作。)
然后v5.6.pl
程序可以做(使用IPC::系统::简单 https://metacpan.org/pod/IPC::System::Simple模块在这里)
use IPC::System::Simple qw(capturex);
...
my $from_524 = capturex($perl_524, 'v5.24.pl', $obj->serialize);
and the v.5.24.pl
然后程序可以做
my ($init_json) = @ARGV;
my $obj = SharedBetweenPerls->new( $init_json );
现在目标程序已经有了一个用所需数据构建的对象并且可以开始工作了。
这里有一个非常基本和粗暴例子。请注意,对于您的项目来说,这可能是一个糟糕的选择,我对此一无所知,而如果可以使用它,则需要更多的工作/检查。
我使用 JSON 来序列化属性及其值的哈希值;这是在serialize
方法。然后这样的 JSON 字符串可以用来构造一个新对象:构造函数检查它是否获得了 hashref 或者字符串,对于字符串,它调用一个方法(init_from_json
) 初始化对象。
sub new { # WARNING: a sketchy demo only
my ($class, $args) = @_;
my $self = {};
bless $self, $class;
my $ref = ref $args;
if (not $ref) { # a string; better be JSON
$self->init_from_json($args);
}
elsif ($ref eq 'HASH') { # straight-up attributes, initialize
$self->{$_} = $args->{$_} for keys %$args;
}
else { croak "Unsupported invocation..." } # print user message etc
return $self;
}
sub serialize {
my $self = shift;
require JSON; JSON->import('encode_json');
my %attr = map { $_ => $self->{$_} } keys %$self;
return encode_json(\%attr); # (no objects please)
}
sub init_from_json {
my ($self, $args_json) = @_;
require JSON; JSON->import('decode_json');
my $args = decode_json($args_json);
$self->{$_} = $args->{$_} for keys %$args;
}
...
Now the v5.6.pl
程序可以创建它的对象并序列化它,然后调用v5.30.pl
将该 JSON 字符串作为输入传递给它的程序。这v5.30.pl
然后程序可以从 JSON 重建对象并使用它来完成工作。
根据具体情况,还有许多其他方法可以做到这一点。
如果您要使用面向对象代码的框架,例如Moose
or Moo
,那么就有现成的工具和技术可以提供帮助。 (如果您没有使用过这些框架,那么还会有一些学习曲线。)
根据要求,完整的工作程序(最大限度地简化以帮助调试) —
v5.6.pl
use warnings;
use strict;
use IPC::System::Simple qw(capturex);
use FindBin qw($RealBin);
use lib $RealBin;
use SharedBetweenPerls;
my $obj = SharedBetweenPerls->new( { roleId => 17, data => [3..7] } );
# (replace my perlbrew path with your actual path to the v5.24 executable)
my $perl_524 = qq($ENV{HOME}/perl5/perlbrew/perls/perl-5.30.0/bin/perl);
my $from_524 = capturex( $perl_524, 'v5.30.pl', $obj->serialize );
print "Return from function: $from_524";
v5.30.pl
use warnings;
use strict;
use v5.24;
use FindBin qw($RealBin);
use lib $RealBin;
use SharedBetweenPerls;
my ($init_json) = @ARGV;
my $obj = SharedBetweenPerls->new($init_json);
print $obj->get_roleId;