如何将具有嵌套属性的 JSON 对象反序列化为 Symfony 实体?

Posted

技术标签:

【中文标题】如何将具有嵌套属性的 JSON 对象反序列化为 Symfony 实体?【英文标题】:How do I deserialize a JSON object - which has a nested property - to a Symfony entity? 【发布时间】:2021-09-21 23:14:45 【问题描述】:

我正在将 JSON 反序列化为一个 php 类(一个 Symfony 实体),它工作正常,但我的 JSON 中有一个嵌套属性,我不知道如何进入 php 类。

PHP 类:

class Vehicle

    private $make:

    /**
     * @SerializedName("meta")
     */
    private $colour;

    // with getters and setters...

// These 2 lines let us use the @SerializedName annotation
$metadataAwareNameConverter = new MetadataAwareNameConverter($classMetadataFactory);
$objectNormalizer = new ObjectNormalizer($classMetadataFactory, $metadataAwareNameConverter);

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

$json = '
    
        "make": "VW Golf",
        "meta": 
            "colour": "red"
        
    
';
$carJson = json_encode($json);

$vehicle = $serializer->deserialize(
    $carJson,
    Vehicle::class,
    'json',
);

$vehicle->getMake(); // VW Golf
$vehicle->getColour(); // ['meta' => ['colour' => 'red']]

...但最后一行应该只返回red

我希望我可以做类似@SerializedName("meta.colour")@SerializedName("[meta][colour]")@SerializedName("meta[colour]") 之类的事情,但这些都决定为空。

Symfony docs on serialization 看起来无法处理这种(简单)案例。

This *** post 也处理嵌套属性,但在他们的示例中,嵌套属性必须反序列化到另一个 php 类,而不是映射到现有的 php 类,所以这对我没有帮助。

在反序列化 JSON 时,如何使 $colour 等于 red

【问题讨论】:

自定义规范化器可能会对您有所帮助。 symfony.com/doc/current/serializer/custom_normalizer.html 正如之前的评论所说,您是否检查了一个自定义规范器,在您的情况下只有一个自定义反序列化函数? 谢谢 - 你说得对,自定义规范器是正确的方法。我发现 Symfony 文档中的示例在这方面没有帮助,我可能会打开 PR 将其添加到文档中。 【参考方案1】:

自定义规范器可以解决问题。代码如下。

<?php

namespace App\Serializer\Denormalizer;

use App\Entity\Vehicle;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;

class VehicleDenormalizer implements CacheableSupportsMethodInterface, ContextAwareDenormalizerInterface

    protected ObjectNormalizer $normalizer;
    protected PropertyAccessor $propertyAccessor;

    public function __construct(ObjectNormalizer $normalizer)
    
        $this->normalizer = $normalizer;
        $this->propertyAccessor = PropertyAccess::createPropertyAccessor();
    

    public function denormalize($data, $type, $format = null, array $context = []): Vehicle
    
        /** @var Vehicle */
        $vehicle = $this->normalizer->denormalize($data, $type, $format, $context);

        // It's possible to directly access the values, but that requires error
        // checking. This method will return a null if it doesn't exist.
        $colour = $this->propertyAccessor->getValue($data, '[meta][colour]');
        $vehicle->setColour($colour);

        return $vehicle;
    

    public function supportsDenormalization($data, $type, $format = null, array $context = [])
    
        return Vehicle::class == $type;
    

    public function hasCacheableSupportsMethod(): bool
    
        return true;
    

要使用此反规范化器,您可以注入 SerializerInterface 或显式创建序列化器(代码如下)。

$json = '
    
        "make": "VW Golf",
        "meta": 
            "colour": "red"
        
    
';
$carJson = json_encode($json);

// These 2 lines let us use the @SerializedName annotation
$metadataAwareNameConverter = new MetadataAwareNameConverter($classMetadataFactory);
$objectNormalizer = new ObjectNormalizer($classMetadataFactory, $metadataAwareNameConverter);

$normalizers = [new VehicleDenormalizer($objectNormalizer)];
$serializer = new Serializer($normalizers);

/** @var Vehicle */
$vehicle = $serializer->denormalize(
    $carJson,
    Vehicle::class,
);

$vehicle->getMake(); // VW Golf
$vehicle->getColour(); // red

我正在显式创建序列化程序,因为由于某种原因,当源数据是字符串而目标数据是整数时,注入方法 not 不会自动转换类型,我得到以下错误:

The type of the "VehicleNo" attribute for class "App\Entity\Vehicle" must be one of "int" ("string" given).

(这个代码示例没有使用VehicleNo,因为我已经对其进行了简化,而是将其包含在此处以显示错误消息的示例,其中Vehicle 具有$vehicleNo 类型的@987654328 属性@)。

【讨论】:

以上是关于如何将具有嵌套属性的 JSON 对象反序列化为 Symfony 实体?的主要内容,如果未能解决你的问题,请参考以下文章

System.Text.Json - 将嵌套对象反序列化为字符串

使用嵌套对象中的属性反序列化 JSON [重复]

将 JSON 反序列化为 C# 对象以在网格中将嵌套数组显示为字符串

将JSON对象反序列化为嵌套的C#对象

Json.NET:将嵌套数组反序列化为强类型对象

将嵌套的 JSON 反序列化为 C# 对象