ksakadia/serializer

对象序列化和反序列化的包装器。

v1.4 2023-11-10 19:50 UTC

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;
    }
}

注意 您需要将 GroupsIgnore 注解加载到您的 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 以讨论您想要更改的内容。

请确保根据需要更新测试。

许可证

MIT