ksakadia / serializer
对象序列化和反序列化的包装器。
Requires
- php: ^7.4||^8.0
- ext-json: *
- doctrine/annotations: ^1.10||^2.0
- phpdocumentor/reflection-docblock: ^5.3
- symfony/property-access: ^5.0||^6.0||^7.0
- symfony/property-info: ^5.0||^6.0||^7.0
- symfony/serializer: ^5.0||^6.0||^7.0
Requires (Dev)
- doctrine/collections: ^1.6||^2.0
- fzaninotto/faker: ^1.9
- phpunit/phpunit: ^8.5||^9.0||^10.0
- tightenco/collect: ^7.5||^8.0||^9.0
This package is auto-updated.
Last update: 2024-09-11 00:05:31 UTC
README
Serializer 是一个用于 Symfony Serializer 的包装库,它帮助序列化和反序列化对象。
[[目录]]
安装
使用 composer 包管理器进行安装。
composer require kaskadia/serializer
用法
假设我们已设置了以下对象。
namespace Kaskadia\Lib\Serializer\Tests\Resources\Objects;
use DateTime;
class User {
/** @var string */
private string $name;
/** @var string */
private string $username;
/** @var int */
private int $age;
/** @var DateTime */
private DateTime $birthDate;
/** @var float */
private float $randomDecimal;
/** @var Company */
private Company $company;
private function __construct(string $name, string $username, int $age, DateTime $birthDate, float $randomDecimal, Company $company) {
$this->setName($name)
->setUsername($username)
->setAge($age)
->setBirthDate($birthDate)
->setRandomDecimal($randomDecimal)
->setCompany($company);
}
public static function initialize(string $name = null, string $username = null, int $age = null, DateTime $birthDate = null, float $randomDecimal = null, Company $company = null) : self {
return new self($name, $username, $age, $birthDate, $randomDecimal, $company);
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @param string $name
* @return User
*/
public function setName(string $name): self
{
if(!isset($this->name)) {
$this->name = $name;
}
return $this;
}
public function getUsername(): string {
return $this->username;
}
public function setUsername(string $username): self {
if(!isset($this->username)) {
$this->username = $username;
}
return $this;
}
/**
* @return int
*/
public function getAge(): int
{
return $this->age;
}
/**
* @param int $age
* @return User
*/
public function setAge(int $age): self
{
if(!isset($this->age)) {
$this->age = $age;
}
return $this;
}
/**
* @return DateTime
*/
public function getBirthDate(): DateTime
{
return $this->birthDate;
}
/**
* @param DateTime $birthDate
* @return User
*/
public function setBirthDate(DateTime $birthDate): self
{
if(!isset($this->birthDate)) {
$this->birthDate = $birthDate;
}
return $this;
}
/**
* @return float
*/
public function getRandomDecimal(): float
{
return $this->randomDecimal;
}
/**
* @param float $randomDecimal
* @return User
*/
public function setRandomDecimal(float $randomDecimal): self
{
if(!isset($this->randomDecimal)) {
$this->randomDecimal = $randomDecimal;
}
return $this;
}
/**
* @return Company
*/
public function getCompany(): Company
{
return $this->company;
}
/**
* @param Company $company
* @return User
*/
public function setCompany(Company $company): self
{
if(!isset($this->company)) {
$this->company = $company;
}
return $this;
}
}
class Company {
/** @var string */
private string $name;
/** @var DateTime */
private DateTime $createdDate;
protected function __construct(string $name, DateTime $createdDate) {
$this->setName($name)
->setCreatedDate($createdDate);
}
public static function initialize(string $name = null, DateTime $createdDate = null) : self {
return new self($name, $createdDate);
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @param string $name
* @return Company
*/
public function setName(string $name): self
{
if(!isset($this->name)) {
$this->name = $name;
}
return $this;
}
/**
* @return DateTime
*/
public function getCreatedDate(): DateTime
{
return $this->createdDate;
}
/**
* @param DateTime $createdDate
* @return Company
*/
public function setCreatedDate(DateTime $createdDate): self
{
if(!isset($this->createdDate)) {
$this->createdDate = $createdDate;
}
return $this;
}
}
使用 serializer 非常直接。如果您查看测试,您会看到一个简单的对象工厂,它允许我们快速生成这些对象的实例并带有测试数据。
namespace Kaskadia\Lib\Serializer\Tests\Resources\Objects\{User,Company};
namespace Kaskadia\Lib\Serializer\Tests\Resources\ObjectFactory;
$factory = new ObjectFactory();
$user = $factory->makeOne(User::class);
$json = $this->serializer->toJson($user);
//json contains
//{"name":"Dr. Noemi Smith DVM","username":"joanie99@hotmail.com","age":18,"birthDate":"1991-04-22 00:25:05.000000 UTC","randomDecimal":8.3836838,"company":{"name":"Ortiz, Frami and Kshlerin","createdDate":"2013-04-20 18:12:31.000000 UTC"}}
$deserializedUser = $this->serializer->fromJson($json, User::class);
// We get back the same object passed in.
使用此 serializer,我们只需要决定想要使用哪种序列化方法。选项有 toJson($data)
和 toXml($data)
。
要将数据 deserialize($data, $className)
反序列化为现有类,我们需要传递数据以及我们想要重建的类名。
数组/集合
serializer 会处理可迭代对象的序列化,通过在序列化对象中创建一个数组。在反序列化这些字符串时,您需要选择以下选项之一: fromJsonArray($data, $className)
或 fromXmlArray($data, $className)
。反序列化过程将返回一个数组,然后如果您想使用集合,如 Laravel 或 Doctrine 的集合,您必须将此数组传递到您喜欢的任何地方。
忽略属性 & 组
有时您有一个包含您不想包含在序列化中的信息的对象。例如,用户可能有一个您不想公开的密码。有几种处理方法,这个库试图使这更容易一些。通过在属性定义中添加 Ignore
注解,您可以轻松确保序列化字符串中不包含该属性。例如,假设我们想在上面示例中的 Company 类中添加 User。但是,如果我们不选择忽略 User
上的 $company
属性或 Company
上的 $user
属性,我们最终会遇到循环引用错误。
这允许您在序列化嵌套对象时排除不希望包含的字段。请参考以下示例,并参阅
use Kaskadia\Lib\Serializer\Tests\Resources\Objects\User;
use Symfony\Component\Serializer\Annotation\Ignore;
use Symfony\Component\Serializer\Annotation\Groups;
class Company {
/**
* @var string
* @Groups({"list_company", "show_company"})
*/
private string $name;
/**
* @var DateTime
* @Groups({"show_company"})
*/
private DateTime $createdDate;
/**
* @var ?User
* @Ignore()
*/
private ?User $user;
...
/**
* If you choose to ignore a property in your serialization, you'll have to adjust
* your get function for that property in order to handle the null state on deserializing the object.
* If you don't, you'll end up with an error that states Typed property {PROPERTY} must not be accessed before initialization.
*/
public getUser(): ?User {
if(!isset($this->user)) {
return null;
}
return $this->user;
}
}
注意 您需要将 Groups
和 Ignore
注解加载到您的 AnnotationReader 中。您也可以根据需要添加更多注解,但这只是一个示例。
在 Laravel 中,添加一个新的 ServiceProvider
并将其加载到 config/app.php
中。
例如,添加文件 app/Providers/SerializerAnnotationsServiceProvider.php
,其内容如下
<?php
namespace App\Providers;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Illuminate\Support\ServiceProvider;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\Ignore;
class SerializerAnnotationsServiceProvider extends ServiceProvider {
public function register() {
AnnotationRegistry::loadAnnotationClass(Groups::class);
AnnotationRegistry::loadAnnotationClass(Ignore::class);
}
}
将新的 ServiceProvider
添加到 config/app.php
中,如下所示
<?php
return [
...
'providers' => [
...
/*
* Application Service Providers ...
*/
...
App\Providers\SerializerAnnotationsServiceProvider::class
],
...
];
Symfony/Serializer 上下文
最后,为了使用 Groups
注解,并且我相信简单的 Ignore()
注解无法满足与该库交互时所需的每个定制,$context[]
属性已被作为可选参数添加到序列化方法 toJson/toXml
中。
namespace Kaskadia\Lib\Serializer\Tests\Resources\Objects\Company;
namespace Kaskadia\Lib\Serializer\Tests\Resources\ObjectFactory;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
$factory = new ObjectFactory();
$company = $factory->makeOne(Company::class);
// In order to use the groups, we have to pass in the context to the serializer
$listJson = $this->serializer->toJson($company, [AbstractNormalizer::GROUPS => ["list_company"]]);
$showJson = $this->serializer->toJson($company, [AbstractNormalizer::GROUPS => ["show_company"]]);
//listJson contains
//{"name":"ACME Ltd."}
//showJson contains
//{"name":"ACME Ltd.","createdDate":"2013-04-20 18:12:31.000000 UTC"}
贡献
欢迎 pull requests。对于重大更改,请首先打开一个 issue 以讨论您想要更改的内容。
请确保根据需要更新测试。