4

我有一个 JSON 字符串,例如

use JSON::XS qw(decode_json);
say Dumper( decode_json($json) );

将产生:

$VAR1 = {
    'Fname' => 'SomeFname',
    'Lname' => 'SomeLname',
    'Addr' => {
          'Street => 'Somestreet',
          'Zip' => '00000',
    },
};

我正在寻找一种简单的方法来将 JSON 字符串(或 perl 结构)“转换”为 Perl/Moose 对象,例如:

 package My;
 use Moose;
 has 'Fname' => (is => 'rw', isa => 'Str');
 has 'Lname' => (is => 'rw', isa => 'Str');
 has 'Addr' =>  (is => 'rw', isa => 'My::Addr');

 package My::Addr;
 use Moose;
 has 'Street' => (is => 'rw', isa => 'Str');
 has 'Zip' => (is => 'rw', isa => 'Str');

问题有两个部分:

  1. 基于 JSON 字符串定义 Moose 类层次结构(一次)
  2. 使用 JSON 中的真实值初始化对象实例(对于每个 JSON)

我对 Moose 不是很熟练,所以需要一些链接来解决这个特定问题。

(Moose 很大——所以阅读CPAN 中的所有内容肯定会有所帮助,但一开始就太多了。因此,我正在寻找在现实世界问题中逐步学习的方法——就像上面一样)。

主要问题是:

  • 是否可以从数据结构生成 Moose 类定义(perl 源)?存在这样的CPAN模块吗?
  • 当我得到类层次结构时(例如,如果这里没有任何帮助,我可以手动编写它们),从 JSON 创建(初始化)它们的实例的最简单方法是什么?
4

1 回答 1

5

是否可以从数据结构生成 Moose 类定义(perl 源)?存在这样的CPAN模块吗?

是的。但是,您如何做到这一点将在很大程度上取决于您希望完成的任务。最简单的方法是在基于 JSON 的内存中构建属性。例如,您可以在运行时从 JSON 文件中实例化元类对象,如下所示:

sub infer_class_from_hash {
    my ($input) = @_;

    # Makes for ugly class names, but does the job
    my $meta = Moose::Meta::Class->create_anon_class;

    for my $key (keys %$input) {
        my $value = $input->{$key};

        my $type;
        my $coerce = 0;

        # Handle nested objects in the JSON as Moose objects
        if (ref $value eq 'HASH') {
            my $inner_meta = infer_class_from_hash($value);
            $type = $meta->name;

            # We provide an automatic HASH -> Class coercion
            $coerce = 1;
        }

        # Assume arrays are always of scalars, could be extended to handle objects
        elsif (ref $value eq 'ARRAY') {
            $type = 'ArrayRef',
        }

        # Assume anything else is string-ish
        else {
            $type = 'Str',
        }

        $meta->add_attribute($key =>
            is => 'rw',
            isa => $type,
            coerce => $coerce,
        );
    }

    # Create a coercion that makes instantiating from the JSON tree dead simple
    use Moose::Util::TypeConstraints;
    coerce $meta->name => from 'HashRef' => via { $meta->name->new($_) };
    no Moose::Util::TypeConstraints;

    return $meta;
}

所有这一切甚至还没有开始进入你可以做的事情。您可以应用角色、自定义基类、添加方法等。但是,这是动态构建 Moose 类的基础。

如果你想要一个代码生成器,将实际的 Moose 类输出为可以构建一次然后加载的文件,那么你可以做任何你想做的事情。我只是编写一个与上面类似的程序,但输出一组.pm文件,其中包含自动生成的 Moose 类定义。

当我得到类层次结构时(例如,如果这里没有任何帮助,我可以手动编写它们),从 JSON 创建(初始化)它们的实例的最简单方法是什么?

use JSON qw( from_json );
my $hash = from_json("...");
my $meta = infer_class_from_hash($hash);

my $obj = $meta->name->new($hash);

The key here is how we use Moose type coercion to automatically convert the JSON objects read in into Moose class instances. If you have more than one serialization scheme or want to use type constraints in some other way, you could setup a base class for all of these (in the call to create_anon_class) that provided to_json/from_json methods to handle the case of nested objects in the JSON input.

于 2012-09-03T20:55:22.007 回答