将单个字符串 属性 反序列化为 symfony JMS 中的对象 PHP

Deserialize single string property into object in symfony JMS PHP

具有 PHP、Symfony 4.4、JMS 序列化程序和 json 有效负载(请求正文):

{
  "quantity": 1,
  "product": {
    "sku": "bla"
  },
  "myId": {
      "id": "0010N00005GcOhhQAF"
  }
}

此有效负载已发送到我的端点,并且所有内容都已正确反序列化为正确的结果 CustomRequest 对象 - 还正确创建了 ProductMyId 等嵌套对象。在 Product 对象的情况下没问题,因为 Product 具有具有多个属性的复杂结构。

但我想实现的是让 myId 的输入更容易。我想要它而不是:

  "myId": {
      "id": "0010N00005GcOhhQAF"
  }

有简单的这个:

  "myId": "0010N00005GcOhhQAF"

你可能会问为什么我有 class 作为简单的 id。它不是简单的 id,它内部有一些特殊的验证和业务逻辑,并在所有应用程序中使用,因此为了验证目的,最好有一个对象。

所以可以说,我希望我的反序列化器自动将那个简单的 id 字符串放入 class MyId 和 return 对象的构造函数中。 MyId class 很简单 class 比如:

class MyId
{
   /**
    * @AppAssert\MyId()
    */
    private ?string $value = null;

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

    public function __toString()
    {
        return $this->value;
    }
}

仅供参考:我在生成的 CustomRequest 对象中尝试了此注释,但它不起作用

/**
 * @Serializer\Type("App\Model\MyId")
 */
private ?MyId $myId = null;

编辑:另一个重要部分:这是端点自动将请求主体转换为 CustomRequest 对象的方式。在这里你可以看到,我正在使用 ParamConverter

    /**
     * @Rest\Post("/product")
     * @ParamConverter("customRequest", options={
     *     "validator"={"groups"={"Default","product"="create"}},
     * })
     */
    public function postCreateProductAction(CustomRequest $customRequest) {
        // ...
    }

问题是:如何将 JMS 序列化程序与 Symfony 一起使用以使其工作。要采用简单的一个字符串,将其自动传递给构造函数并从中生成对象。这可能吗?谢谢

您需要编写自定义 (De)Normalizer。

这是什么:https://symfony.com/doc/current/components/serializer.html#normalizers

自定义方式:https://symfony.com/doc/current/serializer/custom_normalizer.html

我使用 JMS\Serializer\Handler\SubscribingHandlerInterface 让它工作。使用这种方法,您可以简单地添加在 serialization/deserialization 过程中启动的回调。请参阅下面显示 MyId 对象的精确解的代码。

在此处了解有关此技术的更多信息:https://jmsyst.com/libs/serializer/master/handlers

namespace App\Handlers;

use App\Model\MyId;
use JMS\Serializer\Context;
use JMS\Serializer\GraphNavigatorInterface;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\JsonDeserializationVisitor;
use JMS\Serializer\JsonSerializationVisitor;

class MyIdHandler implements SubscribingHandlerInterface
{
    /**
     * @return array<int, array<string, int|string>>
     */
    public static function getSubscribingMethods(): array
    {
        return [
            [
                'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
                'format' => 'json',
                'type' => MyId::class,
                'method' => 'serializeMyIdToJson',
            ],
            [
                'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION,
                'format' => 'json',
                'type' => MyId::class,
                'method' => 'deserializeMyIdFromJson',
            ],
        ];
    }

    /**
     * @param array<mixed> $type
     */
    public function serializeMyIdToJson(JsonSerializationVisitor $visitor, ?MyId $myId, array $type, Context $context): string
    {
        if ($myId === null) {
            return '';
        }

        return (string)$myId;
    }

    /**
     * @param array<mixed> $type
     */
    public function deserializeMyIdFromJson(JsonDeserializationVisitor $visitor, string $myId, array $type, Context $context): ?MyId
    {
        if (empty($myId)) {
            return null;
        }

        return new MyId($myId);
    }
}