JSON 化嵌套的 Perl 对象
JSONizing nested Perl objects
我写了一些 Perl class(包),它有一个方法来 return 一个 JSON 化的对象表示(即:一个 JSON 字符串)。
当重构我的 class 使用另一个 class 的对象时,我不得不调整原来的 JSON-returning 方法。
我的旧 class 使用这样的辅助函数:
use JSON;
sub _JSON_string($$)
{
my $data = shift;
my $JSON = JSON->new(); # JSON encoder
$JSON->utf8(1);
$JSON->allow_blessed(1);
$JSON->convert_blessed(1);
if ($#_ >= 0 && $_[0]) {
$JSON->indent(1);
$JSON->indent_length($_[0]);
}
return $JSON->encode($data);
}
额外的可选参数用于漂亮打印(正如您可能已经猜到的那样)。
而实际的“JSONizer”看起来像这样:
sub JSON_string($;$)
{
my $self = shift;
my $hash = ...;
return _JSON_string($hash, $_[0]);
}
在里面使用的对象里面我写了这段代码JSONize:
use JSON;
sub TO_JSON($)
{
my $self = shift;
my $JSON = JSON->new(); # JSON encoder
$JSON->utf8(1);
$JSON->allow_blessed(1);
$JSON->convert_blessed(1);
return $JSON->encode({ ... });
}
基本上可行,但我想以某种方式将“漂亮的印刷品”传递给 TO_JSON
。
最简单的解决方案是将原始 JSON 对象从包含 class 传递到 TO_JSON
但这不起作用(不允许额外参数)。
所以看来我需要将 JSON 对象从外部对象传递到内部对象以使用通用输出样式。
子问题:
为什么我需要在TO_JSON
里面设置allow_blessed(1)
和convert_blessed(1)
?
该对象没有进一步的祝福组件(...
仅由 inside 祝福对象组成;它们只是数字、字符串或 undef
)。
示例数据
(简化,只是为了显示结构)
我对象的数据是数组,不是散列:
DB<3> x $mp
0 MonitoringParser=ARRAY(0x8e57c0)
0 0
1 'OK'
2 '/etc/group: alpha=0.125, ...'
4 HASH(0x1411608)
'avg' => PerfData=ARRAY(0x1364ca0)
0 'avg'
1 0.00145
2 undef
3 undef
4 undef
5 0
6 undef
'exp_avg' => PerfData=ARRAY(0x12c6c80)
0 'exp_avg'
1 0.00052
2 undef
3 undef
4 undef
5 0
6 undef
'last' => PerfData=ARRAY(0x1549e50)
0 'last'
1 0.00051
2 undef
3 undef
4 undef
5 0
6 undef
DB<6> x $mp->perf_data->{'avg'}
0 PerfData=ARRAY(0x1364ca0)
0 'avg'
1 0.00145
2 undef
3 undef
4 undef
5 0
6 undef
DB<7> x $mp->perf_data->{'avg'}->as_string
0 'label="avg", value=0.00145, unit=<undef>, warn=<undef>, crit=<undef>, min=0, max=<undef>'
所以主要对象是 MonitoringParser
,它在槽 4 有一个带有性能数据的哈希引用。
哈希值是 PerfData
个对象(也是数组)。
我的对象有一个 as_string
方法,可以从对象中进行人类可读的表示。
如您所见,每个数组槽都有一个“图例”(标签)。
我希望 JSON 演示文稿也有这些标签,所以我想动态地从数组中构建一个散列,将键添加到值中(标签可作为数组通过class常数)。
TO_JSON
需要 return 一个数据结构来序列化,它将被序列化为使用该序列化程序的格式 choices/options.
它通常不是 JSON 字符串本身。如果是这样,您最终会发现双重编码 JSON 。这会损害可读性和可用性。
您希望 JSON 序列化程序简单地嵌入您的字符串,这完全没有意义。您不能将任意字符串插入到 JSON 文档中。虽然您的字符串也是 JSON,但它可能与嵌入它的文档不兼容。设置的差异很容易导致文档无效并造成破坏。
正如@ikegami 在回答中提到的,您应该 return 来自 TO_JSON()
的数据结构。这是一个示例,我只是将对数组的祝福引用转换为包含相同数据的简单数组:
use feature qw(say);
use strict;
use warnings;
use JSON;
my $JSON = JSON->new();
$JSON->utf8(1);
$JSON->allow_blessed(1);
$JSON->convert_blessed(1);
my $data = {
foo => "bar",
a => [1, 2, {c => 4}],
hash => { avg => PerfData->new( 'avg', 0.00145 ) }
};
my $json = $JSON->encode($data);
say $json;
package PerfData;
use feature qw(say);
use strict;
use warnings;
sub new {
my ( $class, @args ) = @_;
return bless [@args], $class;
}
sub TO_JSON {
my $self = shift;
return [@$self];
}
输出:
{"a":[1,2,{"c":4}],"hash":{"avg":["avg",0.00145]},"foo":"bar"}
我写了一些 Perl class(包),它有一个方法来 return 一个 JSON 化的对象表示(即:一个 JSON 字符串)。 当重构我的 class 使用另一个 class 的对象时,我不得不调整原来的 JSON-returning 方法。
我的旧 class 使用这样的辅助函数:
use JSON;
sub _JSON_string($$)
{
my $data = shift;
my $JSON = JSON->new(); # JSON encoder
$JSON->utf8(1);
$JSON->allow_blessed(1);
$JSON->convert_blessed(1);
if ($#_ >= 0 && $_[0]) {
$JSON->indent(1);
$JSON->indent_length($_[0]);
}
return $JSON->encode($data);
}
额外的可选参数用于漂亮打印(正如您可能已经猜到的那样)。 而实际的“JSONizer”看起来像这样:
sub JSON_string($;$)
{
my $self = shift;
my $hash = ...;
return _JSON_string($hash, $_[0]);
}
在里面使用的对象里面我写了这段代码JSONize:
use JSON;
sub TO_JSON($)
{
my $self = shift;
my $JSON = JSON->new(); # JSON encoder
$JSON->utf8(1);
$JSON->allow_blessed(1);
$JSON->convert_blessed(1);
return $JSON->encode({ ... });
}
基本上可行,但我想以某种方式将“漂亮的印刷品”传递给 TO_JSON
。
最简单的解决方案是将原始 JSON 对象从包含 class 传递到 TO_JSON
但这不起作用(不允许额外参数)。
所以看来我需要将 JSON 对象从外部对象传递到内部对象以使用通用输出样式。
子问题:
为什么我需要在TO_JSON
里面设置allow_blessed(1)
和convert_blessed(1)
?
该对象没有进一步的祝福组件(...
仅由 inside 祝福对象组成;它们只是数字、字符串或 undef
)。
示例数据
(简化,只是为了显示结构) 我对象的数据是数组,不是散列:
DB<3> x $mp
0 MonitoringParser=ARRAY(0x8e57c0)
0 0
1 'OK'
2 '/etc/group: alpha=0.125, ...'
4 HASH(0x1411608)
'avg' => PerfData=ARRAY(0x1364ca0)
0 'avg'
1 0.00145
2 undef
3 undef
4 undef
5 0
6 undef
'exp_avg' => PerfData=ARRAY(0x12c6c80)
0 'exp_avg'
1 0.00052
2 undef
3 undef
4 undef
5 0
6 undef
'last' => PerfData=ARRAY(0x1549e50)
0 'last'
1 0.00051
2 undef
3 undef
4 undef
5 0
6 undef
DB<6> x $mp->perf_data->{'avg'}
0 PerfData=ARRAY(0x1364ca0)
0 'avg'
1 0.00145
2 undef
3 undef
4 undef
5 0
6 undef
DB<7> x $mp->perf_data->{'avg'}->as_string
0 'label="avg", value=0.00145, unit=<undef>, warn=<undef>, crit=<undef>, min=0, max=<undef>'
所以主要对象是 MonitoringParser
,它在槽 4 有一个带有性能数据的哈希引用。
哈希值是 PerfData
个对象(也是数组)。
我的对象有一个 as_string
方法,可以从对象中进行人类可读的表示。
如您所见,每个数组槽都有一个“图例”(标签)。
我希望 JSON 演示文稿也有这些标签,所以我想动态地从数组中构建一个散列,将键添加到值中(标签可作为数组通过class常数)。
TO_JSON
需要 return 一个数据结构来序列化,它将被序列化为使用该序列化程序的格式 choices/options.
它通常不是 JSON 字符串本身。如果是这样,您最终会发现双重编码 JSON 。这会损害可读性和可用性。
您希望 JSON 序列化程序简单地嵌入您的字符串,这完全没有意义。您不能将任意字符串插入到 JSON 文档中。虽然您的字符串也是 JSON,但它可能与嵌入它的文档不兼容。设置的差异很容易导致文档无效并造成破坏。
正如@ikegami 在回答中提到的,您应该 return 来自 TO_JSON()
的数据结构。这是一个示例,我只是将对数组的祝福引用转换为包含相同数据的简单数组:
use feature qw(say);
use strict;
use warnings;
use JSON;
my $JSON = JSON->new();
$JSON->utf8(1);
$JSON->allow_blessed(1);
$JSON->convert_blessed(1);
my $data = {
foo => "bar",
a => [1, 2, {c => 4}],
hash => { avg => PerfData->new( 'avg', 0.00145 ) }
};
my $json = $JSON->encode($data);
say $json;
package PerfData;
use feature qw(say);
use strict;
use warnings;
sub new {
my ( $class, @args ) = @_;
return bless [@args], $class;
}
sub TO_JSON {
my $self = shift;
return [@$self];
}
输出:
{"a":[1,2,{"c":4}],"hash":{"avg":["avg",0.00145]},"foo":"bar"}