liip / serializer
高性能序列化器,与代码生成的辅助工具协同工作,以实现高吞吐量。
Requires
- php: ^8.0
- ext-json: *
- liip/metadata-parser: ^1.2
- pnz/json-exception: ^1.0
- symfony/filesystem: ^4.4 || ^5.0 || ^6.0 || ^7.0
- symfony/finder: ^4.4 || ^5.0 || ^6.0 || ^7.0
- symfony/options-resolver: ^4.4 || ^5.0 || ^6.0 || ^7.0
- twig/twig: ^2.7 || ^3.0
Requires (Dev)
- doctrine/collections: ^1.6
- friendsofphp/php-cs-fixer: ^3.23
- jms/serializer: ^1.13 || ^2 || ^3
- phpstan/phpstan: ^1.0
- phpstan/phpstan-phpunit: ^1.3
- phpunit/phpunit: ^9.6
- rector/rector: ^1.2.1
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 也提供了 fromArray
和 toArray
方法用于处理数组数据。在通常使用 PHP 数组进行 JSON 数据处理时,您将失去空数组和空对象的区别。
我在哪里寻求帮助?
如果您需要帮助,请在 github 上提交一个 issue。
背景:为什么需要对象序列化生成器?
我们开始遇到大型对象结构(通常为几个兆字节的 JSON 数据)的性能问题。代码分析显示,大量时间花费在调用 JMS 回调结构上。简单生成的 PHP 代码在运行时效率更高。
实现说明
DeserializerGenerator
和 SerializerGenerator
从元数据生成 PHP 代码。生成器使用 twig 来渲染 PHP 代码,以便于阅读。请参阅 Template
命名空间。
生成的代码中的缩进不遵循嵌套级别。我们可以携带深度并前置空白字符,但除了调试之外,没有人会查看生成的代码。
我们决定不使用反射,以获得更好的性能。属性需要是公共的或者有公共的 getter 用于序列化。对于反序列化,我们也会通过名称匹配构造函数参数,因此只要非公共属性名称与构造函数参数匹配,就不需要 setter。