我如何反序列化一个 JSON 对象 - 它有一个嵌套的 属性 - 到一个 Symfony 实体?
How do I deserialize a JSON object - which has a nested property - to a Symfony entity?
我正在将 JSON 反序列化为 php class(一个 Symfony 实体)并且它工作正常,但是我的 [= 中有一个嵌套的 属性 46=] 我不知道如何进入 php class.
PHP class:
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']]
…但是最后一行应该 return 只是 red
.
我希望我可以做类似 @SerializedName("meta.colour")
或 @SerializedName("[meta][colour]")
或 @SerializedName("meta[colour]")
的事情,但每一个都解析为 null。
Symfony docs on serialization 看起来不能处理这个(简单的)案例。
This Whosebug post 也处理嵌套属性,但在他们的示例中,嵌套的 属性 必须反序列化为另一个 php class,而不是映射到现有的 php class,所以这对我没有帮助。
如何在反序列化 JSON 时让 $colour
等于 red
?
自定义规范器可以解决问题。代码如下。
<?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
我明确地创建了序列化器,因为出于某种原因,当源数据是一个字符串而目标是一个整数时,注入方法不会自动转换类型和我收到以下错误:
The type of the "VehicleNo" attribute for class "App\Entity\Vehicle" must be one of "int" ("string" given).
(此代码示例未使用 VehicleNo
,因为我已经对其进行了简化,但将其包含在此处以显示错误消息的示例,例如,Vehicle
有一个 属性 $vehicleNo
类型 int
).
我正在将 JSON 反序列化为 php class(一个 Symfony 实体)并且它工作正常,但是我的 [= 中有一个嵌套的 属性 46=] 我不知道如何进入 php class.
PHP class:
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']]
…但是最后一行应该 return 只是 red
.
我希望我可以做类似 @SerializedName("meta.colour")
或 @SerializedName("[meta][colour]")
或 @SerializedName("meta[colour]")
的事情,但每一个都解析为 null。
Symfony docs on serialization 看起来不能处理这个(简单的)案例。
This Whosebug post 也处理嵌套属性,但在他们的示例中,嵌套的 属性 必须反序列化为另一个 php class,而不是映射到现有的 php class,所以这对我没有帮助。
如何在反序列化 JSON 时让 $colour
等于 red
?
自定义规范器可以解决问题。代码如下。
<?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
我明确地创建了序列化器,因为出于某种原因,当源数据是一个字符串而目标是一个整数时,注入方法不会自动转换类型和我收到以下错误:
The type of the "VehicleNo" attribute for class "App\Entity\Vehicle" must be one of "int" ("string" given).
(此代码示例未使用 VehicleNo
,因为我已经对其进行了简化,但将其包含在此处以显示错误消息的示例,例如,Vehicle
有一个 属性 $vehicleNo
类型 int
).