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"}