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

5

我有一个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中的所有内容肯定有帮助,但作为一个起点来说太多了。因此,我正在寻找在现实世界问题中逐步学习的方法 - 就像上面的问题一样)。
主要问题如下:
1. 是否可能从数据结构生成Moose类定义(perl源代码)?是否存在这样的CPAN模块? 2. 当我得到类层次结构(例如,如果没有任何帮助器,我可以手动编写它们),创建(初始化)它们的实例的最简单方法是什么?
1个回答

6

是否可以从数据结构生成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类输出为文件,这些文件可以构建一次,然后稍后加载,那么您可以随心所欲地做任何事情。我只会编写一个类似于上面的程序,但是输出一组带有自动生成的Moose类定义的.pm文件。
当我获得类层次结构(例如,如果没有助手,我可以手动编写它们),最简单的方式是从JSON创建(初始化)它们的实例是什么?
use JSON qw( from_json );
my $hash = from_json("...");
my $meta = infer_class_from_hash($hash);

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

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


1
非常棒的答案!这将有助于我学习。非常感谢你。 - kobame
顺便提一下,如果想进一步阅读,您也可以查看 Moose 文档中的手册页和可能的食谱页。特别感兴趣的是 Moose::Manual::MOPMoose::Manual::AttributeMoose::Manual::Types - zostay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接