liip/serializer

高性能序列化器,与代码生成的辅助工具协同工作,以实现高吞吐量。

2.6.1 2024-07-19 16:01 UTC

This package is auto-updated.

Last update: 2024-09-19 16:15:17 UTC


README

本项目基于我们最初在Liip作为闭源项目的工作开源,可能缺少一些文档。我们计划在不久的将来添加更多文档和支持,包括Symfony扩展。如果您有任何需求或有任何问题,我们非常愿意看到您提出一个issue! :)

支持的功能

此序列化器可以在JSON和PHP对象之间进行转换,并返回。它使用反射、Phpdoc和JMS Serializer注解来生成PHP代码以进行转换。支持JMS序列化器和版本组进行序列化,但不支持反序列化。

限制

如果您使用自己的监听器或类似的东西自定义了JMS Serializer,则此序列化器将无法为您工作。我们努力检测到使用不受支持的功能时并引发错误,但建议您在转换数据时检查Liip Serializer是否真的与JMS完全相同。

工作原理

Liip Serializer基于您指定的PHP模型生成PHP代码。它使用灵活的liip/metadata-parser来收集模型的元数据。对于每个版本和序列化器组组合生成一个单独的文件,以便将所有逻辑移动到代码生成步骤。由于生成的PHP代码非常简单且针对特定用例,而不是JMS序列化器使用的复杂、灵活的回调结构,因此此序列化器即使对于庞大的对象树也非常快。我们开发的项目经常有高达一兆字节的紧凑JSON数据。

您可以使用Liip Serializer独立使用。如果您已经在使用JMS Serializer,您还可以使用JMS序列化器的即插即用替换liip/serializer-jms-adapter。此即插即用适配器实现了JMS接口,并在缺少生成的文件和其他错误时提供对常规JMS序列化器的回退。

如何使用

每次您的模型更改时,您都需要生成转换文件。它们遵循一个命名方案,允许Liip Serializer找到它们。由于文件必须预先生成,因此您需要指定您想要支持的类、序列化器组和版本的确切列表。

注意:我们计划创建一个Symfony扩展,将Liip Serializer集成到Symfony中。

生成您的文件

此步骤需要在部署阶段以及您的模型更改时执行。

use Doctrine\Common\Annotations\AnnotationReader;
use Liip\MetadataParser\Builder;
use Liip\MetadataParser\Parser;
use Liip\MetadataParser\RecursionChecker;
use Liip\MetadataParser\ModelParser\JMSParser;
use Liip\MetadataParser\ModelParser\LiipMetadataAnnotationParser;
use Liip\MetadataParser\ModelParser\PhpDocParser;
use Liip\MetadataParser\ModelParser\ReflectionParser;
use Liip\Serializer\DeserializerGenerator;
use Liip\Serializer\Serializer;
use Liip\Serializer\SerializerGenerator;
use Liip\Serializer\Template\Deserialization;
use Liip\Serializer\Template\Serialization;

$configuration = GeneratorConfiguration::createFomArray([
    'options' => [
        'allow_generic_arrays' => false,
    ],
    'default_group_combinations' => ['api'],
    'default_versions' => ['', '1', '2'],
    'classes' => [
         Product::class => [
             'default_versions' => ['1', '2'], // optional, falls back to global list
             'group_combinations' => [ // optional, falls back to global default_group_combinations
                 [
                     'groups' => [], // generate without groups
                 ],
                 [
                     'groups' => ['api'], // global groups are overwritten, not merged. versions are taken from class default
                 ],
                 [
                     'groups' => ['api', 'detail'],
                     'versions' => ['2'], // only generate the combination of api and detail for version 2
                 ],
             ],
         ],
         Other::class => [], // generate this class with default groups and versions
    ]
]);

$parsers = [
    new ReflectionParser(),
    new PhpDocParser(),
    new JMSParser(new AnnotationReader()),
    new LiipMetadataAnnotationParser(new AnnotationReader()),
];
$builder = new Builder(new Parser($parsers), new RecursionChecker(null, []));

$serializerGenerator = new SerializerGenerator( new Serialization(), $configuration, $cacheDirectory);
$deserializerGenerator = new DeserializerGenerator(new Deserialization(), [Product::class, User::class], $cacheDirectory);
$serializerGenerator->generate($builder);
$deserializerGenerator->generate($builder);

配置格式

指定要生成的版本的全球默认值以及组组合。

然后指定为哪些类生成序列化器和反序列化器。

对于每个类,您可以覆盖要生成的版本。如果没有指定组组合,则使用全局默认组组合。如果您指定了组组合,则可以再次覆盖要生成的版本。

请注意,默认值不会合并 - 专用列表是唯一使用的。

版本

要生成不带版本的文件,请在版本列表中指定版本''

要生成不带组的序列化器,请指定空组组合[]

未知类型的数组

如果您想序列化/反序列化内容未定义的数组,可以通过设置 options 参数中的 allow_generic_arrays 值为 true 来实现。

注意:此操作仅适用于数组内容仅包含基本类型(字符串、整数、浮点数、布尔值以及仅包含这些类型的嵌套数组)。

使用生成的代码进行序列化

在本例中,我们为版本 2 序列化了一个 Product 类的对象

use Acme\Model\Product;
use Liip\Serializer\Context;
use Liip\Serializer\Serializer;

$serializer = new Serializer($cacheDirectory);

// A model to serialize
$productModel = new Product();

// Your serialized data
$data = $serializer->serialize($productModel, 'json', (new Context())->setVersion(2));

使用生成的代码进行反序列化

use Acme\Model\Product;
use Liip\Serializer\Serializer;

$serializer = new Serializer($cacheDirectory);

// Data to deserialize
$data = '{
    "api_string": "api",
    "detail_string": "details",
    "nested_field": {
        "nested_string": "nested"
    },
    "date": "2018-08-03T00:00:00+02:00",
    "date_immutable": "2016-06-01T00:00:00+02:00"
}';

/** @var Product $model */
$model = $serializer->deserialize($data, Product::class, 'json');

处理数组

与 JMS Serializer 类似,Liip Serializer 也提供了 fromArraytoArray 方法用于处理数组数据。在通常使用 PHP 数组进行 JSON 数据处理时,您将失去空数组和空对象的区别。

我在哪里寻求帮助?

如果您需要帮助,请在 github 上提交一个 issue。

背景:为什么需要对象序列化生成器?

我们开始遇到大型对象结构(通常为几个兆字节的 JSON 数据)的性能问题。代码分析显示,大量时间花费在调用 JMS 回调结构上。简单生成的 PHP 代码在运行时效率更高。

实现说明

DeserializerGeneratorSerializerGenerator 从元数据生成 PHP 代码。生成器使用 twig 来渲染 PHP 代码,以便于阅读。请参阅 Template 命名空间。

生成的代码中的缩进不遵循嵌套级别。我们可以携带深度并前置空白字符,但除了调试之外,没有人会查看生成的代码。

我们决定不使用反射,以获得更好的性能。属性需要是公共的或者有公共的 getter 用于序列化。对于反序列化,我们也会通过名称匹配构造函数参数,因此只要非公共属性名称与构造函数参数匹配,就不需要 setter。