如何使用 Symfony Serializer 将 DateTime 数组非规范化为 DateTime 对象

问题描述 投票:0回答:2

我需要使用 symfony 6 对该数据进行非规范化

array:4 [
  "createdAt" => array:3 [
    "date" => "2024-01-09 17:04:37.209330"
    "timezone_type" => 3
    "timezone" => "Europe/Paris"
  ]
  "utilisateur" => "phpunit"
  "type" => "CREATION"
  "texte" => "creation de la demande"
]

这个物体

class Historique
{
    public \DateTime $createdAt;

    public function __construct(public readonly string $utilisateur, public readonly string $type, public readonly ?string $texte)
    {
        $this->createdAt = new \DateTime();
    }

    public function getTypeLabel(): string
    {
        return HistoriqueTypeEnum::getLabelName($this->type);
    }
}

我已经使用了这段代码,但我在对

DateTime
对象进行非规范化时遇到问题。

$normalizers = [
    new DateTimeNormalizer(),
    new ObjectNormalizer(null, null, null, new ReflectionExtractor()),
    new BackedEnumNormalizer(),
];
$serializer = new Serializer($normalizers, [new JsonEncoder()]);
$serializer->denormalize($historique, Historique::class, 'json', [DateTimeNormalizer::FORMAT_KEY => 'Y-m-d H:i:s',]);

我收到此错误:

数据要么不是字符串,要么是空字符串,要么是 null;您应该传递一个可以使用传递的格式或有效的日期时间字符串进行解析的字符串。

如果我像这样改变标准化器的顺序:

$normalizers = [
    new ObjectNormalizer(null, null, null, new ReflectionExtractor()),
    new BackedEnumNormalizer(),
    new DateTimeNormalizer(),
];

我收到此错误:

无法从序列化数据创建“DateTimeZone”实例,因为其构造函数需要存在以下参数:“$timezone”。”)

symfony serialization denormalization
2个回答
0
投票

DateTimeNormalizer 类需要一个有效的 DateTime 字符串。您可以更改数据数组并将createdAt键设置为日期字符串,然后使用

DateTimeNormalizer::TIMEZONE_KEY

设置时区,而不是忽略DateTime对象
$historique = [
  "createdAt" => [
    "date" => "2024-01-09 17:04:37.209330",
    "timezone_type" => 3,
    "timezone" => "Europe/Paris"
  ],
  "utilisateur" => "phpunit",
  "type" => "CREATION",
  "texte" => "creation de la demande",
];

$createdAt = $historique['createdAt'];
$historique['createdAt'] = $createdAt['date'];

$normalizers = [
    new DateTimeNormalizer(),
    new ObjectNormalizer(null, null, null, new ReflectionExtractor()),
    new BackedEnumNormalizer(),
];
$serializer = new Serializer($normalizers, [new JsonEncoder()]);
$object = $serializer->denormalize(
    $historique, 
    Historique::class, 
    'json', 
    [
        DateTimeNormalizer::TIMEZONE_KEY => $createdAt['timezone'],
    ]
);
dump($object);

// Result:
// ^ Historique^ {#114
//   +createdAt: DateTime @1704816277 {#104
//     date: 2024-01-09 17:04:37.209330 Europe/Paris (+01:00)
//   }
//   +utilisateur: "phpunit"
//   +type: "CREATION"
//   +texte: "creation de la demande"
// }

0
投票

如果您的示例数组是输入,您需要一个单独的反规范化器来满足您的要求。

我的日期时间类型

要识别该属性,请创建您自己的类型。该类型继承\DateTime。

namespace App\Normalizer;

class MyDateTimeType extends \DateTime { }

历史

class Historique
{
    public MyDateTimeType $createdAt;

    public function __construct(public readonly string $utilisateur, public readonly string $type, public readonly ?string $texte)
    {
        $this->createdAt = new MyDateTimeType();
    }

    public function getTypeLabel(): string
    {
        return $this->type;
    }
}

MyDateTimeTypeDenormalizer

继承 DateTimeNormaliser 用于规范化过程。

namespace App\Normalizer;

use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class MyDateTimeTypeDenormalizer extends DateTimeNormalizer implements DenormalizerInterface
{
    public function denormalize(mixed $data, string $type, string $format = null, array $context = []): \DateTimeInterface
    {
        return new MyDateTimeType($data['date'], new \DateTimeZone($data['timezone']));
    }

    public function supportsDenormalization(mixed $data, string $type, string $format = null): bool
    {
        return MyDateTimeType::class === $type;
    }
}

示例+测试

use App\Normalizer\Historique;
use App\Normalizer\MyDateTimeTypeDenormalizer;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;

class HistoriqueTest extends KernelTestCase
{
    public function testHistorique()
    {
        $historique = [
            "createdAt" => [
                "date" => "2024-01-09 17:04:37.209330",
                "timezone_type" => 3,
                "timezone" => "Europe/Paris"
            ],
            "utilisateur" => "phpunit",
            "type" => "CREATION",
            "texte" => "creation de la demande"
        ];

        $normalizers = [
            new MyDateTimeTypeDenormalizer(),
            new ObjectNormalizer(
                new ClassMetadataFactory(new AnnotationLoader(null)),
                null,
                null,
                new ReflectionExtractor()
            ),
            new BackedEnumNormalizer()
        ];

        $serializer = new Serializer($normalizers, [new JsonEncoder()]);

        $result = $serializer->denormalize(
           $historique,
           Historique::class,
           'json',
           [DateTimeNormalizer::FORMAT_KEY => 'Y-m-d H:i:s']
        );

        $this->assertSame('2024-01-09T17:04:37+01:00', $result->createdAt->format('c'));
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.