consistence-community/consistence-jms-serializer

Consistence 库与 JMS Serializer 的集成

2.2.1 2021-11-05 11:21 UTC

This package is auto-updated.

Last update: 2024-09-12 16:25:42 UTC


README

此包是 consistence/consistence-jms-serializer 的分支,由社区维护,以支持新的 PHP 版本。

此库提供了 Consistence 值对象与 JMS Serializer 的集成,以便您可以在序列化映射中使用它们。

目前,所需的唯一集成是 枚举(Enums),以下是一些示例。

使用方法

枚举(Enums)代表一组预定义的值,当然,您也希望对这些值进行序列化和反序列化。由于 枚举(Enums) 是对象,并且您只想(反)序列化表示的值,因此必须有一些映射。

您可以在以下示例中看到,您想要(反)序列化 User 对象中的性别。

<?php

namespace Consistence\JmsSerializer\Example\User;

class Sex extends \Consistence\Enum\Enum
{

	public const FEMALE = 'female';
	public const MALE = 'male';

}

现在您可以在 User 对象中使用 Sex 枚举。类型指定为 enum<Your\Enum\Class>

<?php

namespace Consistence\JmsSerializer\Example\User;

use JMS\Serializer\Annotation as JMS;

class User extends \Consistence\ObjectPrototype
{

	// ...

	/**
	 * @JMS\Type("enum<Consistence\JmsSerializer\Example\User\Sex>")
	 * @var \Consistence\JmsSerializer\Example\User\Sex|null
	 */
	private $sex;

	// ...

	public function __construct(
		// ...
		Sex $sex = null
		// ...
	)
	{
		// ...
		$this->sex = $sex;
		// ...
	}

}

现在一切准备就绪,当您序列化对象时,只有 female 将作为枚举的值返回

<?php

namespace Consistence\JmsSerializer\Example\User;

/** @var \JMS\Serializer\Serializer $serializer */
$user = new User(Sex::get(Sex::FEMALE));
var_dump($serializer->serialize($user, 'json'));

/*

{
   "sex": "female"
}

*/

当您反序列化对象时,您将再次收到 Sex 枚举对象

<?php

namespace Consistence\JmsSerializer\Example\User;

/** @var \JMS\Serializer\Serializer $serializer */
var_dump($serializer->deserialize('{
   "sex": "female"
}', User::class, 'json'));

/*

class Consistence\JmsSerializer\Example\User\User#46 (1) {
  private $sex =>
  class Consistence\JmsSerializer\Example\User\Sex#5 (1) {
    private $value =>
    string(6) "female"
  }
}

*/

这意味着对象的 API 是对称的(您得到与您设置相同类型),您可以开始从枚举的优势中受益,例如确保您得到的是一个有效的值,并且可以在表示的值之上定义方法。

默认为可空

序列化和反序列化都将接受 null 值,没有特殊的映射(按照 Jms Serializer 的约定),因此如果您期望非空值,您必须通过其他方式强制执行此操作 - 要么通过限制对象 API,要么通过验证(尤其是在反序列化时)。

无效值

在序列化时,不应该存在无效值,因为 枚举(Enums) 保证实例中只包含有效值。

在反序列化时,可能给出一个无效值,将抛出异常

<?php

namespace Consistence\JmsSerializer\Example\User;

/** @var \JMS\Serializer\Serializer $serializer */
var_dump($serializer->deserialize('{
   "sex": "FOO"
}', User::class, 'json'));

// \Consistence\Enum\InvalidEnumValueException: FOO [string] is not a valid value, accepted values: female, male

如果您在 API 中使用此功能,请确保您将捕获此异常并向消费者发送详细说明此错误的响应,您也可以编写自定义消息,可用的值列在 InvalidEnumValueException::getAvailableValues() 中。

XML 支持

与 JSON 不同,在 XML 中无法直接从值推断值类型。因此,如果您需要反序列化 XML,您必须手动提供此类型。您可以通过在类型定义中写入类型来完成此操作 - 对于上面的示例,它将是 string

<?php

namespace Consistence\JmsSerializer\Example\User;

use JMS\Serializer\Annotation as JMS;

class User extends \Consistence\ObjectPrototype
{

	// ...

	/**
	 * @JMS\Type("enum<Consistence\JmsSerializer\Example\User\Sex, string>")
	 * @var \Consistence\JmsSerializer\Example\User\Sex|null
	 */
	private $sex;

	// ...

}

对映射的 MultiEnums 的特殊支持

由于反序列化操作只与枚举所表示的值相关,因此在多枚举的情况下,这意味着输出内部位掩码的值。如果客户端和服务器使用相同的枚举对象,这可能很有用,否则这会破坏抽象,并且对于人类消费者来说可读性也更差。

如果您使用的是映射到单个枚举的多枚举,提供了一个方便的解决方案,如果您在映射中添加了enum<Your\Enum\Class, as_single> - 注意新的as_single参数,那么MultiEnum的值将序列化为单个Enum值的集合

<?php

namespace Consistence\JmsSerializer\Example\User;

use Consistence\Type\ArrayType\ArrayType;

use JMS\Serializer\Annotation as JMS;

class RoleEnum extends \Consistence\Enum\Enum
{

	public const USER = 'user';
	public const EMPLOYEE = 'employee';
	public const ADMIN = 'admin';

}

class RolesEnum extends \Consistence\Enum\MultiEnum
{

	/** @var int[] format: single Enum value (string) => MultiEnum value (int) */
	private static $singleMultiMap = [
		RoleEnum::USER => 1,
		RoleEnum::EMPLOYEE => 2,
		RoleEnum::ADMIN => 4,
	];

	public static function getSingleEnumClass(): string
	{
		return RoleEnum::class;
	}

	/**
	 * Converts value representing a value from single Enum to MultiEnum counterpart
	 *
	 * @param string $singleEnumValue
	 * @return int
	 */
	protected static function convertSingleEnumValueToValue($singleEnumValue): int
	{
		return ArrayType::getValue(self::$singleMultiMap, $singleEnumValue);
	}

	/**
	 * Converts value representing a value from MultiEnum to single Enum counterpart
	 *
	 * @param int $value
	 * @return string
	 */
	protected static function convertValueToSingleEnumValue(int $value): string
	{
		return ArrayType::getKey(self::$singleMultiMap, $value);
	}

}

class User extends \Consistence\ObjectPrototype
{

	// ...

	/**
	 * @JMS\Type("enum<Consistence\JmsSerializer\Example\User\RolesEnum, as_single>")
	 * @var \Consistence\JmsSerializer\Example\User\RolesEnum
	 */
	private $roles;

	// ...

	public function __construct(
		// ...
		RolesEnum $roles
		// ...
	)
	{
		// ...
		$this->roles = $roles;
		// ...
	}

}

$user = new User(RolesEnum::getMultiByEnums([
	RoleEnum::get(RoleEnum::USER),
	RoleEnum::get(RoleEnum::ADMIN),
]));

/** @var \JMS\Serializer\Serializer $serializer */
var_dump($serializer->serialize($user, 'json'));

/*

{
   "roles": [
      "user",
      "admin"
   ]
}

*/

反序列化同样也是对称的 - 提供单个Enum值的数组将生成一个MultiEnum实例

<?php

namespace Consistence\JmsSerializer\Example\User;

/** @var \JMS\Serializer\Serializer $serializer */
var_dump($serializer->deserialize('{
   "roles": [
      "user",
      "admin"
   ]
}', User::class, 'json'));

/*

class Consistence\JmsSerializer\Example\User\User#48 (1) {
  private $roles =>
  class Consistence\JmsSerializer\Example\User\RolesEnum#37 (1) {
    private $value =>
    int(5)
  }
}

*/

安装

  1. 使用Composer安装包consistence-community/consistence-jms-serializer
composer require consistence-community/consistence-jms-serializer
  1. 注册序列化处理器
<?php

use Consistence\JmsSerializer\Enum\EnumSerializerHandler;

use JMS\Serializer\Handler\HandlerRegistry;
use JMS\Serializer\SerializerBuilder;

$serializer = SerializerBuilder::create()
	->configureHandlers(function (HandlerRegistry $registry) {
		$registry->registerSubscribingHandler(new EnumSerializerHandler());
	})
	->build();

如果您使用的是Symfony,您可以在config/services.yaml文件中注册处理器

services:
    Consistence\JmsSerializer\Enum\EnumSerializerHandler:
        tags:
            - { name: jms_serializer.subscribing_handler }

到此为止,您就可以开始使用了!