@@ -56,6 +56,13 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
56
56
*/
57
57
public const DISABLE_TYPE_ENFORCEMENT = 'disable_type_enforcement ' ;
58
58
59
+ /**
60
+ * While denormalizing, convert scalar types to the expected type.
61
+ *
62
+ * Enabled by default for the XML and CSV format, because all basic datatypes are represented as strings.
63
+ */
64
+ public const ENABLE_TYPE_CONVERSION = 'enable_type_conversion ' ;
65
+
59
66
/**
60
67
* Flag to control whether fields with the value `null` should be output
61
68
* when normalizing or omitted.
@@ -290,8 +297,6 @@ abstract protected function extractAttributes(object $object, string $format = n
290
297
291
298
/**
292
299
* Gets the attribute value.
293
- *
294
- * @return mixed
295
300
*/
296
301
abstract protected function getAttributeValue (object $ object , string $ attribute , string $ format = null , array $ context = []);
297
302
@@ -305,9 +310,6 @@ public function supportsDenormalization(mixed $data, string $type, string $forma
305
310
return class_exists ($ type ) || (interface_exists ($ type , false ) && null !== $ this ->classDiscriminatorResolver ?->getMappingForClass($ type ));
306
311
}
307
312
308
- /**
309
- * @return mixed
310
- */
311
313
public function denormalize (mixed $ data , string $ type , string $ format = null , array $ context = [])
312
314
{
313
315
if (!isset ($ context ['cache_key ' ])) {
@@ -320,6 +322,10 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
320
322
return null ;
321
323
}
322
324
325
+ if (!isset ($ context [self ::ENABLE_TYPE_CONVERSION ]) && (XmlEncoder::FORMAT === $ format || CsvEncoder::FORMAT === $ format )) {
326
+ $ context [self ::ENABLE_TYPE_CONVERSION ] = true ;
327
+ }
328
+
323
329
$ allowedAttributes = $ this ->getAllowedAttributes ($ type , $ context , true );
324
330
$ normalizedData = $ this ->prepareForDenormalization ($ data );
325
331
$ extraAttributes = [];
@@ -434,11 +440,16 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
434
440
$ isUnionType = \count ($ types ) > 1 ;
435
441
$ extraAttributesException = null ;
436
442
$ missingConstructorArgumentsException = null ;
443
+ $ hasNonObjectType = false ;
444
+ $ isUnionTypeOrNullable = $ isUnionType ;
445
+
437
446
foreach ($ types as $ type ) {
438
447
if (null === $ data && $ type ->isNullable ()) {
439
448
return null ;
440
449
}
441
450
451
+ $ isUnionTypeOrNullable = $ isUnionTypeOrNullable ?: $ type ->isNullable ();
452
+ $ hasNonObjectType = $ hasNonObjectType ?: Type::BUILTIN_TYPE_OBJECT !== $ type ->getBuiltinType ();
442
453
$ collectionValueType = $ type ->isCollection () ? $ type ->getCollectionValueTypes ()[0 ] ?? null : null ;
443
454
444
455
// Fix a collection that contains the only one element
@@ -456,7 +467,7 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
456
467
// In XML and CSV all basic datatypes are represented as strings, it is e.g. not possible to determine,
457
468
// if a value is meant to be a string, float, int or a boolean value from the serialized representation.
458
469
// That's why we have to transform the values, if one of these non-string basic datatypes is expected.
459
- if (\is_string ($ data ) && (XmlEncoder:: FORMAT === $ format || CsvEncoder:: FORMAT === $ format )) {
470
+ if (\is_string ($ data ) && ($ context [ self :: ENABLE_TYPE_CONVERSION ] ?? false )) {
460
471
if ('' === $ data ) {
461
472
if (Type::BUILTIN_TYPE_ARRAY === $ builtinType = $ type ->getBuiltinType ()) {
462
473
return [];
@@ -564,17 +575,17 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
564
575
return $ data ;
565
576
}
566
577
} catch (NotNormalizableValueException |InvalidArgumentException $ e ) {
567
- if (!$ isUnionType ) {
578
+ if (!$ isUnionTypeOrNullable ) {
568
579
throw $ e ;
569
580
}
570
581
} catch (ExtraAttributesException $ e ) {
571
- if (!$ isUnionType ) {
582
+ if (!$ isUnionTypeOrNullable ) {
572
583
throw $ e ;
573
584
}
574
585
575
586
$ extraAttributesException ??= $ e ;
576
587
} catch (MissingConstructorArgumentsException $ e ) {
577
- if (!$ isUnionType ) {
588
+ if (!$ isUnionTypeOrNullable ) {
578
589
throw $ e ;
579
590
}
580
591
@@ -590,6 +601,12 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
590
601
throw $ missingConstructorArgumentsException ;
591
602
}
592
603
604
+ if ('' === $ data && !$ hasNonObjectType && ($ context [self ::ENABLE_TYPE_CONVERSION ] ?? false )) {
605
+ return null ;
606
+ } elseif (!$ isUnionType && isset ($ 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
}
0 commit comments