8000 bug #21165 [Serializer] int is valid when float is expected when dese… · symfony/symfony@c36f25f · GitHub
[go: up one dir, main page]

Skip to content

Commit c36f25f

Browse files
committed
bug #21165 [Serializer] int is valid when float is expected when deserializing JSON (dunglas)
This PR was squashed before being merged into the 3.1 branch (closes #21165). Discussion ---------- [Serializer] int is valid when float is expected when deserializing JSON | Q | A | ------------- | --- | Branch? | 3.1 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | api-platform/api-platform#37 | License | MIT | Doc PR | n/a JSON only has a Number type corresponding to both `int` and `float` PHP types. PHP's `json_encode`, JavaScript's `JSON.stringify`, Go's `json.Marshal` as well as most other JSON encoders convert floating-point numbers like `12.0` to `12` (the decimal part is dropped when possible). PHP's `json_decode` automatically converts Numbers without a decimal part to integers. Actually, the Serializer rejects integers when a float is expected, this PR fixes this behavior when denormalizing JSON-based formats. Port of api-platform/core#714. /cc @gorghoa @Shine-neko Commits ------- 4125455 [Serializer] int is valid when float is expected when deserializing JSON
2 parents d7928ea + 4125455 commit c36f25f

File tree

2 files changed

+34
-5
lines changed

2 files changed

+34
-5
lines changed

src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Serializer\Normalizer;
1313

1414
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
15+
use Symfony\Component\Serializer\Encoder\JsonEncoder;
1516
use Symfony\Component\Serializer\Exception\CircularReferenceException;
1617
use Symfony\Component\Serializer\Exception\LogicException;
1718
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
@@ -260,6 +261,16 @@ private function validateAndDenormalize($currentClass, $attribute, $data, $forma
260261
}
261262
}
262263

264+
// JSON only has a Number type corresponding to both int and float PHP types.
265+
// PHP's json_encode, JavaScript's JSON.stringify, Go's json.Marshal as well as most other JSON encoders convert
266+
// floating-point numbers like 12.0 to 12 (the decimal part is dropped when possible).
267+
// PHP's json_decode automatically converts Numbers without a decimal part to integers.
268+
// To circumvent this behavior, integers are converted to floats when denormalizing JSON based formats and when
269+
// a float is expected.
270+
if (Type::BUILTIN_TYPE_FLOAT === $builtinType && is_int($data) && false !== strpos($format, JsonEncoder::FORMAT)) {
271+
return (float) $data;
272+
}
273+
263274
if (call_user_func('is_'.$builtinType, $data)) {
264275
return $data;
265276
}

src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -537,11 +537,21 @@ public function testDenomalizeRecursive()
537537
'inners' => array(array('foo' => 1), array('foo' => 2)),
538538
), ObjectOuter::class);
539539

540-
$this->assertEquals('foo', $obj->getInner()->foo);
541-
$this->assertEquals('bar', $obj->getInner()->bar);
542-
$this->assertEquals('1988-01-21', $obj->getDate()->format('Y-m-d'));
543-
$this->assertEquals(1, $obj->getInners()[0]->foo);
544-
$this->assertEquals(2, $obj->getInners()[1]->foo);
540+
$this->assertSame('foo', $obj->getInner()->foo);
541+
$this->assertSame('bar', $obj->getInner()->bar);
542+
$this->assertSame('1988-01-21', $obj->getDate()->format('Y-m-d'));
543+
$this->assertSame(1, $obj->getInners()[0]->foo);
544+
$this->assertSame(2, $obj->getInners()[1]->foo);
545+
}
546+
547+
public function testAcceptJsonNumber()
548+
{
549+
$extractor = new PropertyInfoExtractor(array(), array(new PhpDocExtractor(), new ReflectionExtractor()));
550+
$normalizer = new ObjectNormalizer(null, null, null, $extractor);
551+
$serializer = new Serializer(array(new ArrayDenormalizer(), new DateTimeNormalizer(), $normalizer));
552+
553+
$this->assertSame(10.0, $serializer->denormalize(array('number' => 10), JsonNumber::class, 'json')->number);
554+
$this->assertSame(10.0, $serializer->denormalize(array('number' => 10), JsonNumber::class, 'jsonld')->number);
545555
}
546556

547557
/**
@@ -820,3 +830,11 @@ protected function isAllowedAttribute($classOrObject, $attribute, $format = null
820830
return false;
821831
}
822832
}
833+
834+
class JsonNumber
835+
{
836+
/**
837+
* @var float
838+
*/
839+
public $number;
840+
}

0 commit comments

Comments
 (0)
0