hamlet-framework/json-mapper

Json 数据绑定

0.0.5 2021-02-16 09:55 UTC

This package is auto-updated.

Last update: 2024-08-29 05:30:55 UTC


README

CI Status Packagist Packagist Coverage Status Psalm coverage

快速概要

  • Psalm 类型规范,包括对象数组、联合类型、关联数组等。
  • PHP-Parser 用于解析 FQCN
  • 默认使用反射
  • 可重用的代码配置
  • 通过子类型解析器支持多态
  • 级联配置选项用于子树解析
  • 类型安全:Psalm 将知道 JsonMapper::map(_list(_class(User::class)), ...) 返回 list<User>
  • 抛出类型转换异常

作为一个开始,将以下 JSON 结构

[
    { "name": "Yuri" },
    { "name": "Oleg", "email": "oleg@example.com", "address": { "city": "Vologda" } }
]

映射到以下类层次结构

<?php
 
class User
{
    /** @var string */
    private $name;

    /** @var string|null */
    private $email;

    /** @var Address|null */
    private $address;
}

class Address 
{
    /** @var string */
    private $city;
}

使用

<?php

$users = JsonMapper::map(
    _list(_class(User::class)), 
    json_decode($data)
);

该库使用 hamlet-framework/type 库进行类型规范。

配置选项

JsonMapper::map 的第三个参数是 JsonMapperConfiguration,用于自定义映射过程。

Json 属性

<?php

$configuration
    ->withDefaultValue(User::class, 'name', 'unknown')
    ->withJsonName(User::class, 'homeAddress', 'home_address', 'homeaddress')
    ->ignoreUnknown(User::class);

使用 Setters

<?php

$configuaration
    ->withPropertySetters(User::class)
    ->withPropertySetter(User::class, 'homeAddress', 'updateHomeAddress');

使用 Converter

<?php

class User 
{
    /** @var DateTimeImmutable */
    private $time;

    /** @var array<string,string> */
    private $preferences;

    /** @var string|null */
    private $email;
}

$json = '
    { "time": 1593479541, "preferences": "{\"timeZone\":\"Russia/Moscow\"}", "email": "_.oO000_" }
';

$configuration
    ->withConverter(User::class, 'time', function (int $unixtime) {
        return DateTimeImmutable::createFomFormat('U', (string) $unixtime);
    })
    ->withConverter(User::class, 'preferences', function (string $json) {
        return _map(_string(), _string())->cast(json_decode($json)); 
    })
    ->withConverter(self::class, 'email', function ($email) {
        return filter_var($email, FILTER_VALIDATE_EMAIL) ?: null;
    });

$user = JsonMapper::map(_class(User::class), json_decode($json), $configuration);

$user->preferences['timeZone'] == 'Russia/Moscow';
$user->time instanceof DateTimeImmutable;
$user->email === null;

使用 Type Dispatcher

<?php

$configuration
    ->withTypeDispatcher(User::class, function ($properties) {
        if (isset($properties['name'])) {
            return NamedUser::class;
        } else {
            return AnonymousUser::class;
        }
    });
<?php

$coniguration
    ->withTypeDispatcher(User::class, '__resolveType');

使用 JsonMapperAware 接口

如果您想将映射配置保持得与您映射的文件更接近,有一个选项可以实现 JsonMapperAware 接口

<?php

class Car implements JsonMapperAware
{
    /** @var string */
    protected $make;

    public function make(): string
    {
        return $this->make;
    }

    public static function configureJsonMapper(JsonMapperConfiguration $configuration): JsonMapperConfiguration
    {
        return $configuration
            ->withTypeResolver(self::class, function ($properties) {
                if (array_key_exists('machineGunCapacity', (array) $properties)) {
                    return JamesBondCar::class;
                } else {
                    return Car::class;
                }
            });
    }
}

$cars = JsonMapper::map(_list(_class(Car::class)), json_decode($payload));

待办事项

  • 添加对 ignoreUnknown 的支持
  • 添加对构造函数方法的支持
  • 添加验证器
  • 添加带有 psalm 规范的示例