如何使用 JMS Serializer EventSubscriberInterface (php, symfony) 将对象序列化为它自己的属性数组

问题描述

我需要序列化一个对象作为它自己的属性(它的类型是数组),我的意思是该对象有一个数组属性books,在转换它之后我想跳过books键,所以结构会更平坦 [book1,book2](不是 [books => [book1,book2]]。我有以下类:

<?php

class Store
{
    private ?BooksCollection $booksCollection = null;

    public function __construct(?BooksCollection $booksCollection = null)
    {
        $this->booksCollection = $booksCollection;
    }

    public function getBooksCollection(): ?BooksCollection
    {
        return $this->booksCollection;
    }
}

class BooksCollection
{
    /** @var Book[] */
    private array $books;

    public function __construct(Book ...$books)
    {
        $this->books = $books;
    }

    public function getBooks(): array
    {
        return $this->books;
    }
}

class Book
{
    private string $title;

    public function __construct(string $title)
    {
        $this->title = $title;
    }

    public function getTitle(): string
    {
        return $this->title;
    }
}

和序列化配置:

Store:
  exclusion_policy: ALL
  properties:
    booksCollection:
      type: BooksCollection
BooksCollection:
  exclusion_policy: ALL
  properties:
    books:
      type: array<int,Book>
Book:
  exclusion_policy: ALL
  properties:
    title:
      type: string

我想通过的测试:

<?php

use JMS\Serializer\ArrayTransformerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

class StoreSerializeTest extends KernelTestCase
{
    /** @var ArrayTransformerInterface */
    private $serializer;

    protected function setUp(): void
    {
        self::bootKernel();
        $this->serializer = self::$kernel->getContainer()->get('jms_serializer');
    }

    public function testSerialization(): void
    {
        $store = new Store(new BooksCollection(new Book('Birdy'),new Book('Lotr')));

        $serializedStore = $this->serializer->toArray($store);
        $storeUnserialized = $this->serializer->fromArray($serializedStore,Store::class);

        self::assertSame(
            [
                'books_collection' => [
                    ['title' => 'Birdy'],['title' => 'Lotr']
                ]
            ],$serializedStore
        );
        self::assertEquals($store,$storeUnserialized);
    }
}

如下所示,测试失败。我怎样才能摆脱一本嵌套的“书”?

jms serializer

我的主要想法是使用 EventSubscriberInterfaceonPreSerialize 事件,但我真的不知道如何用由其组成的数组替换对象 BooksCollection自己的财产books。有没有人已经知道怎么做?

解决方法

我终于明白了。我实施了SubscribingHandlerInterface

<?php

use JMS\Serializer\Context;
use JMS\Serializer\GraphNavigatorInterface;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\JsonDeserializationVisitor;
use JMS\Serializer\JsonSerializationVisitor;
use Book;
use BooksCollection;

class BooksCollectionHandler implements SubscribingHandlerInterface
{
    public static function getSubscribingMethods(): array
    {
        return [
            [
                'type' => BooksCollection::class,'format' => 'json','method' => 'serialize','direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,],[
                'type' => BooksCollection::class,'method' => 'deserialize','direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION,]
        ];
    }

    public function serialize(
        JsonSerializationVisitor $visitor,BooksCollection $booksCollection,array $type,Context $context
    ) {
        return $visitor->visitArray($booksCollection->getBooks(),['name' => 'array'],$context);
    }

    public function deserialize(
        JsonDeserializationVisitor $visitor,array $data,Context $context
    ): BooksCollection {
        $collection = [];

        foreach ($data as $book) {
            $collection[] =
                $visitor->getNavigator()->accept($book,['name' => Book::class],$context);
        }

        return new BooksCollection(...$collection);
    }
}

服务配置:

    books_handler:
        class: BooksCollectionHandler
        tags:
            - { name: jms_serializer.subscribing_handler }

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...