@@ -185,9 +185,12 @@ public function normalize(mixed $object, string $format = null, array $context =
185
185
}
186
186
187
187
$ attributeContext = $ this ->getAttributeNormalizationContext ($ object , $ attribute , $ context );
188
+ $ discriminatorMapping = $ this ->classDiscriminatorResolver ?->getMappingForMappedObject($ object );
188
189
189
190
try {
190
- $ attributeValue = $ this ->getAttributeValue ($ object , $ attribute , $ format , $ attributeContext );
191
+ $ attributeValue = $ attribute === $ discriminatorMapping ?->getTypeProperty()
192
+ ? $ discriminatorMapping
193
+ : $ this ->getAttributeValue ($ object , $ attribute , $ format , $ attributeContext );
191
194
} catch (UninitializedPropertyException $ e ) {
192
195
if ($ context [self ::SKIP_UNINITIALIZED_VALUES ] ?? $ this ->defaultContext [self ::SKIP_UNINITIALIZED_VALUES ] ?? true ) {
193
196
continue ;
@@ -258,22 +261,18 @@ protected function getAttributes(object $object, ?string $format, array $context
258
261
return $ this ->attributesCache [$ key ];
259
262
}
260
263
261
- $ allowedAttributes = $ this ->getAllowedAttributes ($ object , $ context , true );
262
-
263
- if (false !== $ allowedAttributes ) {
264
- if ($ context ['cache_key ' ]) {
265
- $ this ->attributesCache [$ key ] = $ allowedAttributes ;
266
- }
267
-
268
- return $ allowedAttributes ;
269
- }
270
-
271
264
$ attributes = $ this ->extractAttributes ($ object , $ format , $ context );
272
265
273
266
if ($ mapping = $ this ->classDiscriminatorResolver ?->getMappingForMappedObject($ object )) {
274
267
array_unshift ($ attributes , $ mapping ->getTypeProperty ());
275
268
}
276
269
270
+ $ allowedAttributes = $ this ->getAllowedAttributes ($ object , $ context , true );
271
+
272
+ if (false !== $ allowedAttributes ) {
273
+ $ attributes = array_intersect ($ attributes , $ allowedAttributes );
274
+ }
275
+
277
276
if ($ context ['cache_key ' ] && \stdClass::class !== $ class ) {
278
277
$ this ->attributesCache [$ key ] = $ attributes ;
279
278
}
@@ -364,8 +363,12 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
364
363
}
365
364
366
365
if ($ attributeContext [self ::DEEP_OBJECT_TO_POPULATE ] ?? $ this ->defaultContext [self ::DEEP_OBJECT_TO_POPULATE ] ?? false ) {
366
+ $ discriminatorMapping = $ this ->classDiscriminatorResolver ?->getMappingForMappedObject($ object );
367
+
367
368
try {
368
- $ attributeContext [self ::OBJECT_TO_POPULATE ] = $ this ->getAttributeValue ($ object , $ attribute , $ format , $ attributeContext );
369
+ $ attributeContext [self ::OBJECT_TO_POPULATE ] = $ attribute === $ discriminatorMapping ?->getTypeProperty()
370
+ ? $ discriminatorMapping
371
+ : $ this ->getAttributeValue ($ object , $ attribute , $ format , $ attributeContext );
369
372
} catch (NoSuchPropertyException ) {
370
373
}
371
374
}
@@ -432,8 +435,10 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
432
435
{
433
436
$ expectedTypes = [];
434
437
$ isUnionType = \count ($ types ) > 1 ;
438
+ $ e = null ;
435
439
$ extraAttributesException = null ;
436
440
$ missingConstructorArgumentsException = null ;
441
+ $ isNullable = false ;
437
442
foreach ($ types as $ type ) {
438
443
if (null === $ data && $ type ->isNullable ()) {
439
444
return null ;
@@ -456,18 +461,22 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
456
461
// In XML and CSV all basic datatypes are represented as strings, it is e.g. not possible to determine,
457
462
// if a value is meant to be a string, float, int or a boolean value from the serialized representation.
458
463
// That's why we have to transform the values, if one of these non-string basic datatypes is expected.
464
+ $ builtinType = $ type ->getBuiltinType ();
459
465
if (\is_string ($ data ) && (XmlEncoder::FORMAT === $ format || CsvEncoder::FORMAT === $ format )) {
460
466
if ('' === $ data ) {
461
- if (Type::BUILTIN_TYPE_ARRAY === $ builtinType = $ type -> getBuiltinType () ) {
467
+ if (Type::BUILTIN_TYPE_ARRAY === $ builtinType ) {
462
468
return [];
463
469
}
464
470
465
- if ($ type -> isNullable () && \in_array ( $ builtinType , [ Type::BUILTIN_TYPE_BOOL , Type:: BUILTIN_TYPE_INT , Type:: BUILTIN_TYPE_FLOAT ], true ) ) {
466
- return null ;
471
+ if (Type::BUILTIN_TYPE_STRING === $ builtinType ) {
472
+ return '' ;
467
473
}
474
+
475
+ // Don't return null yet because Object-types that come first may accept empty-string too
476
+ $ isNullable = $ isNullable ?: $ type ->isNullable ();
468
477
}
469
478
470
- switch ($ builtinType ?? $ type -> getBuiltinType () ) {
479
+ switch ($ builtinType ) {
471
480
case Type::BUILTIN_TYPE_BOOL :
472
481
// according to https://www.w3.org/TR/xmlschema-2/#boolean, valid representations are "false", "true", "0" and "1"
473
482
if ('false ' === $ data || '0 ' === $ data ) {
@@ -564,24 +573,28 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
564
573
return $ data ;
565
574
}
566
575
} catch (NotNormalizableValueException |InvalidArgumentException $ e ) {
567
- if (!$ isUnionType ) {
576
+ if (!$ isUnionType && ! $ isNullable ) {
568
577
throw $ e ;
569
578
}
570
579
} catch (ExtraAttributesException $ e ) {
571
- if (!$ isUnionType ) {
580
+ if (!$ isUnionType && ! $ isNullable ) {
572
581
throw $ e ;
573
582
}
574
583
575
584
$ extraAttributesException ??= $ e ;
576
585
} catch (MissingConstructorArgumentsException $ e ) {
577
- if (!$ isUnionType ) {
586
+ if (!$ isUnionType && ! $ isNullable ) {
578
587
throw $ e ;
579
588
}
580
589
581
590
$ missingConstructorArgumentsException ??= $ e ;
582
591
}
583
592
}
584
593
594
+ if ($ isNullable ) {
595
+ return null ;
596
+ }
597
+
585
598
if ($ extraAttributesException ) {
586
599
throw $ extraAttributesException ;
587
600
}
@@ -590,6 +603,10 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
590
603
throw $ missingConstructorArgumentsException ;
591
604
}
592
605
606
+ if (!$ isUnionType && $ e ) {
607
+ throw $ e ;
608
+ }
609
+
593
610
if ($ context [self ::DISABLE_TYPE_ENFORCEMENT ] ?? $ this ->defaultContext [self ::DISABLE_TYPE_ENFORCEMENT ] ?? false ) {
594
611
return $ data ;
595
612
}
@@ -629,7 +646,7 @@ private function getTypes(string $currentClass, string $attribute): ?array
629
646
return $ this ->typesCache [$ key ] = $ types ;
630
647
}
631
648
632
- if (null !== $ this -> classDiscriminatorResolver && null !== $ discriminatorMapping = $ this ->classDiscriminatorResolver ->getMappingForClass ($ currentClass )) {
649
+ if ($ discriminatorMapping = $ this ->classDiscriminatorResolver ? ->getMappingForClass($ currentClass )) {
633
650
if ($ discriminatorMapping ->getTypeProperty () === $ attribute ) {
634
651
return $ this ->typesCache [$ key ] = [
635
652
new Type (Type::BUILTIN_TYPE_STRING ),
0 commit comments