将JSON字符串转换为Perl/Moose对象

kob*_*ame 4 perl serialization json code-generation moose

我有一个JSON字符串,例如

use JSON::XS qw(decode_json);
say Dumper( decode_json($json) );
Run Code Online (Sandbox Code Playgroud)

将产生:

$VAR1 = {
    'Fname' => 'SomeFname',
    'Lname' => 'SomeLname',
    'Addr' => {
          'Street => 'Somestreet',
          'Zip' => '00000',
    },
};
Run Code Online (Sandbox Code Playgroud)

我正在寻找一种简单的方法,如何将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');
Run Code Online (Sandbox Code Playgroud)

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

问题分为两部分:

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

我对Moose不是很熟练,所以需要一些链接来研究这个特殊问题.

(Moose很大 - 所以阅读CPAN中的所有内容肯定会有所帮助,但这对于一开始来说太过分了.因此,我正在寻找在现实世界中逐步学习的问题 - 如上所述).

主要问题是:

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

zos*_*tay 5

有可能从数据结构中生成Moose类definitoins(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;
}
Run Code Online (Sandbox Code Playgroud)

所有这一切甚至都没有开始进入你能做的事情.您可以应用角色,自定义基类,添加方法等.但是,这是构建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);
Run Code Online (Sandbox Code Playgroud)

这里的关键是我们如何使用Moose类型强制来自动将读入的JSON对象转换为Moose类实例.如果你有多个序列化方案或者想以某种其他方式使用类型约束,你可以为所有这些(在调用中create_anon_class)设置一个基类,它提供to_json/ from_json方法来处理JSON输入中嵌套对象的情况.