如何为 Symfony 序列化程序使 属性 可为空
How to make property nullable for Symfony Serializer
我正在尝试反序列化为具有 属性 的对象,该对象可能将对象数组作为值或 null
.
我对数组反序列化没问题,但我需要将 null 反序列化为空数组或 null 本身。
例如{ "items": null }
class A {
/**
* @var null|Item[]
*/
private $items = [];
/**
* @return Item[]|null
*/
public function getItems(): ?array
{
return $this->items ?? [];
}
/**
* @param Item $param
* @return A
*/
public function addItem(Item $param)
{
if (!is_array($this->items)) $this->items = [];
if (!in_array($param, $this->items))
$this->items[] = $param;
return $this;
}
// /** tried with this as well
// * @param array|null $param
// * @return A
// */
// public function setItems(?array $param)
// {
// $this->items = $param ?? [];
// return $this;
// }
/**
* @param Item $item
* @return A
*/
public function removeItem(Item $item): A
{
if (!is_array($this->items)) $this->items = [];
if (in_array($item, $this->items))
unset($this->items[array_search($item, $this->items)]);
return $this;
}
/**
* @param Item $item
* @return bool
*/
public function hasItem(Item $item): bool
{
return in_array($item, $this->items);
}
}
序列化器看起来像这样
$defaultContext = [
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER =>
function ($articles, $format, $context) {
return $articles->getId();
},
AbstractObjectNormalizer::SKIP_NULL_VALUES => false
];
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$metadataAwareNameConverter = new MetadataAwareNameConverter($classMetadataFactory);
$encoders = [new JsonEncoder()];
$serializer = new Serializer([
new ArrayDenormalizer(),
new DateTimeNormalizer(),
new ObjectNormalizer($classMetadataFactory, $metadataAwareNameConverter, null,
new ReflectionExtractor(), null, null, $defaultContext
),
], $encoders);
$a = $serializer->deserialize('{ "items": null }', A::class, 'json');
items
为 null 时出现的错误
[Symfony\Component\Serializer\Exception\InvalidArgumentException]
Data expected to be an array, null given.
是否可以为空 属性?
追踪到 Serializer 源代码并找到了三个可能的选项来拥有一个可为 null 的数组。
选项 1
删除 addItem
、hasItem
、removeItem
方法,它允许设置 null、数组等。就我而言,这是不太受欢迎的解决方案。
选项 2
添加构造函数也有帮助。 https://github.com/symfony/serializer/blob/5.3/Normalizer/AbstractNormalizer.php#L381
/**
* A constructor.
* @param array|null $items
*/
public function __construct($items)
{
$this->items = $items ?? [];
}
选项 3
扩展 ArrayDenormalizer
并覆盖 denormalize
方法来处理空值
public function denormalize($data, string $type, string $format = null, array $context = []): array
{
if (null === $this->denormalizer) {
throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!');
}
if (!\is_array($data) && !is_null($data)) {
throw new InvalidArgumentException('Data expected to be an array or null, ' . get_debug_type($data) . ' given.');
}
if (!str_ends_with($type, '[]')) {
throw new InvalidArgumentException('Unsupported class: ' . $type);
}
if(is_null($data))
return [];
$type = substr($type, 0, -2);
$builtinType = isset($context['key_type']) ? $context['key_type']->getBuiltinType() : null;
foreach ($data as $key => $value) {
if (null !== $builtinType && !('is_' . $builtinType)($key)) {
throw new NotNormalizableValueException(sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, $builtinType, get_debug_type($key)));
}
$data[$key] = $this->denormalizer->denormalize($value, $type, $format, $context);
}
return $data;
}
我正在尝试反序列化为具有 属性 的对象,该对象可能将对象数组作为值或 null
.
我对数组反序列化没问题,但我需要将 null 反序列化为空数组或 null 本身。
例如{ "items": null }
class A {
/**
* @var null|Item[]
*/
private $items = [];
/**
* @return Item[]|null
*/
public function getItems(): ?array
{
return $this->items ?? [];
}
/**
* @param Item $param
* @return A
*/
public function addItem(Item $param)
{
if (!is_array($this->items)) $this->items = [];
if (!in_array($param, $this->items))
$this->items[] = $param;
return $this;
}
// /** tried with this as well
// * @param array|null $param
// * @return A
// */
// public function setItems(?array $param)
// {
// $this->items = $param ?? [];
// return $this;
// }
/**
* @param Item $item
* @return A
*/
public function removeItem(Item $item): A
{
if (!is_array($this->items)) $this->items = [];
if (in_array($item, $this->items))
unset($this->items[array_search($item, $this->items)]);
return $this;
}
/**
* @param Item $item
* @return bool
*/
public function hasItem(Item $item): bool
{
return in_array($item, $this->items);
}
}
序列化器看起来像这样
$defaultContext = [
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER =>
function ($articles, $format, $context) {
return $articles->getId();
},
AbstractObjectNormalizer::SKIP_NULL_VALUES => false
];
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$metadataAwareNameConverter = new MetadataAwareNameConverter($classMetadataFactory);
$encoders = [new JsonEncoder()];
$serializer = new Serializer([
new ArrayDenormalizer(),
new DateTimeNormalizer(),
new ObjectNormalizer($classMetadataFactory, $metadataAwareNameConverter, null,
new ReflectionExtractor(), null, null, $defaultContext
),
], $encoders);
$a = $serializer->deserialize('{ "items": null }', A::class, 'json');
items
为 null 时出现的错误
[Symfony\Component\Serializer\Exception\InvalidArgumentException]
Data expected to be an array, null given.
是否可以为空 属性?
追踪到 Serializer 源代码并找到了三个可能的选项来拥有一个可为 null 的数组。
选项 1
删除 addItem
、hasItem
、removeItem
方法,它允许设置 null、数组等。就我而言,这是不太受欢迎的解决方案。
选项 2
添加构造函数也有帮助。 https://github.com/symfony/serializer/blob/5.3/Normalizer/AbstractNormalizer.php#L381
/**
* A constructor.
* @param array|null $items
*/
public function __construct($items)
{
$this->items = $items ?? [];
}
选项 3
扩展 ArrayDenormalizer
并覆盖 denormalize
方法来处理空值
public function denormalize($data, string $type, string $format = null, array $context = []): array
{
if (null === $this->denormalizer) {
throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!');
}
if (!\is_array($data) && !is_null($data)) {
throw new InvalidArgumentException('Data expected to be an array or null, ' . get_debug_type($data) . ' given.');
}
if (!str_ends_with($type, '[]')) {
throw new InvalidArgumentException('Unsupported class: ' . $type);
}
if(is_null($data))
return [];
$type = substr($type, 0, -2);
$builtinType = isset($context['key_type']) ? $context['key_type']->getBuiltinType() : null;
foreach ($data as $key => $value) {
if (null !== $builtinType && !('is_' . $builtinType)($key)) {
throw new NotNormalizableValueException(sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, $builtinType, get_debug_type($key)));
}
$data[$key] = $this->denormalizer->denormalize($value, $type, $format, $context);
}
return $data;
}