Skip to content

Commit 5a2db0a

Browse files
authored
Merge pull request #27 from square/nullableprops
Fix issues with nullable properties
2 parents 06c0c4c + ac942a4 commit 5a2db0a

File tree

5 files changed

+77
-19
lines changed

5 files changed

+77
-19
lines changed

src/Json.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public function forProperty(ReflectionProperty $prop) : Json
6161
/**
6262
* Builds the PHP value from the json data and a type if available
6363
*/
64-
public function retrieveValue(array $data, ?ReflectionNamedType $type = null)
64+
public function retrieveValue(?array $data, ?ReflectionNamedType $type = null)
6565
{
6666
foreach ($this->path as $pathBit) {
6767
if (!array_key_exists($pathBit, $data)) {
@@ -70,6 +70,10 @@ public function retrieveValue(array $data, ?ReflectionNamedType $type = null)
7070
$data = $data[$pathBit];
7171
}
7272

73+
if (is_null($data) && $type && $type->allowsNull()) {
74+
return null;
75+
}
76+
7377
if ($type === null) {
7478
if (isset($this->type)) {
7579
$t = $this->type;

src/JsonSerialize.php

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use ReflectionAttribute;
66
use Square\Pjson\Internal\RClass;
7-
use stdClass;
87
use const JSON_THROW_ON_ERROR;
98

109
trait JsonSerialize

tests/DeSerializationTest.php

+33
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public function testDeserializesSimpleClass()
6363
"name" => "Clothes",
6464
"data_name" => null,
6565
"schedule" => null,
66+
"nullableSchedule" => null,
6667
"schedules" => null,
6768
"nullableSchedules" => null,
6869
"counts" => [],
@@ -71,6 +72,32 @@ public function testDeserializesSimpleClass()
7172
], $this->export($c));
7273
}
7374

75+
public function testNullableProperty()
76+
{
77+
$c = Category::fromJsonString('{
78+
"identifier": "myid",
79+
"category_name": "Clothes",
80+
"data": {
81+
"name": null
82+
},
83+
"nullable_schedule": null
84+
}');
85+
$this->assertEquals([
86+
'@class' => Category::class,
87+
"id" => "myid",
88+
"name" => "Clothes",
89+
"data_name" => null,
90+
"schedule" => null,
91+
"nullableSchedule" => null,
92+
"schedules" => null,
93+
"nullableSchedules" => null,
94+
"counts" => [],
95+
"unnamed" => null,
96+
"untypedSchedule" => null,
97+
], $this->export($c));
98+
$this->assertNull($c->nullableSchedule);
99+
}
100+
74101
public function testThrowsOnError()
75102
{
76103
$this->expectException(\JsonException::class);
@@ -89,6 +116,7 @@ public function testOmitsEmptyValues()
89116
"id" => "myid",
90117
"name" => "Clothes",
91118
"schedule" => null,
119+
"nullableSchedule" => null,
92120
"schedules" => null,
93121
"nullableSchedules" => null,
94122
"counts" => [],
@@ -130,6 +158,7 @@ public function testSerializesClassAttributesRecursively()
130158
"start" => 1,
131159
"end" => 20,
132160
],
161+
"nullableSchedule" => null,
133162
"schedules" => null,
134163
"nullableSchedules" => null,
135164
"counts" => [],
@@ -172,6 +201,7 @@ public function testSerializesObjectArrays()
172201
"start" => 1,
173202
"end" => 20,
174203
],
204+
"nullableSchedule" => null,
175205
"schedules" => [
176206
0 => [
177207
'@class' => Schedule::class,
@@ -211,6 +241,7 @@ public function testSerializesScalarArrays()
211241
"name" => "Clothes",
212242
"data_name" => null,
213243
"schedule" => null,
244+
"nullableSchedule" => null,
214245
"schedules" => null,
215246
"nullableSchedules" => null,
216247
"counts" => [
@@ -240,6 +271,7 @@ public function testSerializesWithNoName()
240271
"name" => "Clothes",
241272
"data_name" => null,
242273
"schedule" => null,
274+
"nullableSchedule" => null,
243275
"schedules" => null,
244276
"nullableSchedules" => null,
245277
"counts" => [],
@@ -269,6 +301,7 @@ public function testSerializesWithUntypedProp()
269301
"name" => "Clothes",
270302
"data_name" => null,
271303
"schedule" => null,
304+
"nullableSchedule" => null,
272305
"schedules" => null,
273306
"nullableSchedules" => null,
274307
"counts" => [],

tests/Definitions/Category.php

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ class Category
2222
#[Json('next_schedule')]
2323
protected Schedule $schedule;
2424

25+
#[Json('nullable_schedule')]
26+
public ?Schedule $nullableSchedule;
27+
2528
#[Json('untyped_schedule', type: Schedule::class, omit_empty: true)]
2629
public $untypedSchedule;
2730

tests/SerializationTest.php

+36-17
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,20 @@ public function testSerializesSimpleClass()
2727
$this->assertEquals('{"identifier":"myid","category_name":"Clothes","data":{"name":null}}', $c->toJson());
2828
}
2929

30+
public function testNullableProperty()
31+
{
32+
$c = new Category;
33+
$c->nullableSchedule = null;
34+
$this->assertEquals($this->comparableJson('{
35+
"identifier": "myid",
36+
"category_name": "Clothes",
37+
"data": {
38+
"name": null
39+
},
40+
"nullable_schedule": null
41+
}'), $c->toJson());
42+
}
43+
3044
public function testSerializeAcceptsJsonFlags()
3145
{
3246
$c = new Category;
@@ -78,7 +92,7 @@ public function testSerializesClassAttributesRecursively()
7892
{
7993
$c = new Category;
8094
$c->setSchedule(new Schedule(1, 20));
81-
$this->assertEquals(json_encode(json_decode('{
95+
$this->assertEquals($this->comparableJson('{
8296
"identifier": "myid",
8397
"category_name": "Clothes",
8498
"data": {
@@ -88,7 +102,7 @@ public function testSerializesClassAttributesRecursively()
88102
"schedule_start": 1,
89103
"schedule_end": 20
90104
}
91-
}')), $c->toJson());
105+
}'), $c->toJson());
92106
}
93107

94108
public function testSerializesObjectArrays()
@@ -97,7 +111,7 @@ public function testSerializesObjectArrays()
97111
$c->setSchedule(new Schedule(1, 20));
98112
$c->setUpcoming([new Schedule(1, 20), new Schedule(30, 40)]);
99113
$c->setNullable();
100-
$this->assertEquals(json_encode(json_decode('{
114+
$this->assertEquals($this->comparableJson('{
101115
"identifier": "myid",
102116
"category_name": "Clothes",
103117
"data": {
@@ -118,14 +132,14 @@ public function testSerializesObjectArrays()
118132
}
119133
],
120134
"nullable_schedules": null
121-
}')), $c->toJson());
135+
}'), $c->toJson());
122136
}
123137

124138
public function testSerializesScalarArrays()
125139
{
126140
$c = new Category;
127141
$c->counts = [1, 'abc', 678];
128-
$this->assertEquals(json_encode(json_decode('{
142+
$this->assertEquals($this->comparableJson('{
129143
"identifier": "myid",
130144
"category_name": "Clothes",
131145
"data": {
@@ -136,35 +150,35 @@ public function testSerializesScalarArrays()
136150
"abc",
137151
678
138152
]
139-
}')), $c->toJson());
153+
}'), $c->toJson());
140154
}
141155

142156
public function testSerializesWithNoName()
143157
{
144158
$c = new Category;
145159
$c->unnamed = 'bob';
146-
$this->assertEquals(json_encode(json_decode('{
160+
$this->assertEquals($this->comparableJson('{
147161
"identifier": "myid",
148162
"category_name": "Clothes",
149163
"data": {
150164
"name": null
151165
},
152166
"unnamed": "bob"
153-
}')), $c->toJson());
167+
}'), $c->toJson());
154168
}
155169

156170
public function testPrivateProps()
157171
{
158172
$p = new Privateer;
159-
$this->assertEquals(json_encode(json_decode('{
173+
$this->assertEquals($this->comparableJson('{
160174
"name": "Jenna"
161-
}')), $p->toJson());
175+
}'), $p->toJson());
162176
}
163177

164178
public function testHashMaps()
165179
{
166180
$w = new Weekend;
167-
$this->assertEquals(json_encode(json_decode('{
181+
$this->assertEquals($this->comparableJson('{
168182
"weekend": {
169183
"sat": {
170184
"schedule_start": 1,
@@ -175,13 +189,13 @@ public function testHashMaps()
175189
"schedule_end": 4
176190
}
177191
}
178-
}')), $w->toJson());
192+
}'), $w->toJson());
179193
}
180194

181195
public function testTraitProps()
182196
{
183197
$t = new Traitor;
184-
$this->assertEquals(json_encode(json_decode('{"secretly_working_for": "MI6"}')), $t->toJson());
198+
$this->assertEquals($this->comparableJson('{"secretly_working_for": "MI6"}'), $t->toJson());
185199
}
186200

187201
public function testPolymorphicClass()
@@ -206,7 +220,7 @@ public function testList()
206220
];
207221

208222
$jl = Schedule::toJsonList($l);
209-
$this->assertEquals(json_encode(json_decode('[
223+
$this->assertEquals($this->comparableJson('[
210224
{
211225
"schedule_start": 1,
212226
"schedule_end": 2
@@ -219,7 +233,7 @@ public function testList()
219233
"schedule_start": 111,
220234
"schedule_end": 222
221235
}
222-
]')), $jl);
236+
]'), $jl);
223237
}
224238

225239
public function testClassToScalar()
@@ -285,7 +299,7 @@ public function testCollections()
285299

286300
$data = Collector::fromJsonString($json);
287301

288-
$this->assertEquals(json_encode(json_decode($json)), $data->toJson());
302+
$this->assertEquals($this->comparableJson($json), $data->toJson());
289303
}
290304

291305
public function testMissingParent()
@@ -295,6 +309,11 @@ public function testMissingParent()
295309
$data = new Child();
296310
$json = '{"identifier":null,"parent":{"id":null}}';
297311

298-
$this->assertEquals(json_encode(json_decode($json)), $data->toJson());
312+
$this->assertEquals($this->comparableJson($json), $data->toJson());
313+
}
314+
315+
protected function comparableJson(string $json) : string
316+
{
317+
return json_encode(json_decode($json));
299318
}
300319
}

0 commit comments

Comments
 (0)