Skip to content

Commit b145fbf

Browse files
committed
fix: Allow default values for typed promoted properties & ignore missing values for nullable properties (set as null by default)
1 parent 3815102 commit b145fbf

File tree

4 files changed

+79
-12
lines changed

4 files changed

+79
-12
lines changed

src/GoodPhp/Serialization/TypeAdapter/Primitive/ClassProperties/Property/DefaultBoundClassPropertyFactory.php

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ class DefaultBoundClassPropertyFactory implements BoundClassPropertyFactory
1010
public function create(PropertyReflection $property, string $serializedName, string $typeAdapterType, Serializer $serializer): BoundClassProperty
1111
{
1212
return OptionalSkippingBoundClassProperty::wrap(
13-
DefaultValueSkippingBoundClassProperty::wrap(
14-
DirectlyBoundClassProperty::from($property, $serializedName, $typeAdapterType, $serializer)
13+
NullSkippingBoundClassProperty::wrap(
14+
DefaultValueSkippingBoundClassProperty::wrap(
15+
DirectlyBoundClassProperty::from($property, $serializedName, $typeAdapterType, $serializer)
16+
)
1517
)
1618
);
1719
}

src/GoodPhp/Serialization/TypeAdapter/Primitive/ClassProperties/Property/DefaultValueSkippingBoundClassProperty.php

+12-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace GoodPhp\Serialization\TypeAdapter\Primitive\ClassProperties\Property;
44

5+
use Closure;
56
use GoodPhp\Reflection\Reflector\Reflection\PropertyReflection;
67
use GoodPhp\Serialization\TypeAdapter\Primitive\ClassProperties\MissingValueException;
78

@@ -14,16 +15,21 @@ class DefaultValueSkippingBoundClassProperty implements BoundClassProperty
1415
{
1516
public function __construct(
1617
private readonly BoundClassProperty $delegate,
18+
private readonly Closure $defaultValue,
1719
) {
1820
}
1921

2022
public static function wrap(BoundClassProperty $property): BoundClassProperty
2123
{
22-
if (!$property->reflection()->hasDefaultValue()) {
23-
return $property;
24+
if ($property->reflection()->hasDefaultValue()) {
25+
return new self($property, fn () => $property->reflection()->defaultValue());
2426
}
2527

26-
return new self($property);
28+
if ($property->reflection()->promotedParameter() && $property->reflection()->promotedParameter()->hasDefaultValue()) {
29+
return new self($property, fn () => $property->reflection()->promotedParameter()->hasDefaultValue());
30+
}
31+
32+
return $property;
2733
}
2834

2935
public function reflection(): PropertyReflection
@@ -52,7 +58,9 @@ public function deserialize(array $data): array
5258
try {
5359
return $this->delegate->deserialize($data);
5460
} catch (MissingValueException) {
55-
return [];
61+
return [
62+
$this->reflection()->name() => ($this->defaultValue)(),
63+
];
5664
}
5765
}
5866
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
namespace GoodPhp\Serialization\TypeAdapter\Primitive\ClassProperties\Property;
4+
5+
use GoodPhp\Reflection\Reflector\Reflection\PropertyReflection;
6+
use GoodPhp\Reflection\Type\Special\NullableType;
7+
use GoodPhp\Serialization\TypeAdapter\Primitive\ClassProperties\MissingValueException;
8+
9+
/**
10+
* Skips nullable fields if their value is missing.
11+
*
12+
* @template T
13+
*/
14+
class NullSkippingBoundClassProperty implements BoundClassProperty
15+
{
16+
public function __construct(
17+
private readonly BoundClassProperty $delegate,
18+
) {
19+
}
20+
21+
public static function wrap(BoundClassProperty $property): BoundClassProperty
22+
{
23+
if (!$property->reflection()->type() instanceof NullableType) {
24+
return $property;
25+
}
26+
27+
return new self($property);
28+
}
29+
30+
public function reflection(): PropertyReflection
31+
{
32+
return $this->delegate->reflection();
33+
}
34+
35+
public function serializedName(): string
36+
{
37+
return $this->delegate->serializedName();
38+
}
39+
40+
/**
41+
* @inheritDoc
42+
*/
43+
public function serialize(object $object): array
44+
{
45+
return $this->delegate->serialize($object);
46+
}
47+
48+
/**
49+
* @inheritDoc
50+
*/
51+
public function deserialize(array $data): array
52+
{
53+
try {
54+
return $this->delegate->deserialize($data);
55+
} catch (MissingValueException) {
56+
return [
57+
$this->reflection()->name() => null,
58+
];
59+
}
60+
}
61+
}

tests/Integration/JsonSerializationTest.php

+2-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use GoodPhp\Serialization\TypeAdapter\Exception\UnexpectedEnumValueException;
1717
use GoodPhp\Serialization\TypeAdapter\Exception\UnexpectedValueTypeException;
1818
use GoodPhp\Serialization\TypeAdapter\Json\JsonTypeAdapter;
19-
use GoodPhp\Serialization\TypeAdapter\Primitive\ClassProperties\MissingValueException;
2019
use GoodPhp\Serialization\TypeAdapter\Primitive\ClassProperties\PropertyMappingException;
2120
use Illuminate\Support\Collection;
2221
use PHPUnit\Framework\TestCase;
@@ -336,7 +335,7 @@ public function deserializesProvider(): Generator
336335
empty_optional(),
337336
null,
338337
),
339-
'{"primitive":1,"nested":{},"date":"2020-01-01T00:00:00.000+00:00","nullable":null}',
338+
'{"primitive":1,"nested":{},"date":"2020-01-01T00:00:00.000+00:00"}',
340339
];
341340
}
342341

@@ -479,10 +478,7 @@ public function deserializesWithAnExceptionProvider(): Generator
479478
];
480479

481480
yield 'ClassStub with wrong primitive type' => [
482-
new MultipleMappingException([
483-
new PropertyMappingException('primitive', new UnexpectedValueTypeException('1', PrimitiveType::integer())),
484-
new PropertyMappingException('nullable', new MissingValueException()),
485-
]),
481+
new PropertyMappingException('primitive', new UnexpectedValueTypeException('1', PrimitiveType::integer())),
486482
new NamedType(
487483
ClassStub::class,
488484
new Collection([

0 commit comments

Comments
 (0)