[Serializer] int is valid when float is expected when deserializing JSON

This commit is contained in:
Kévin Dunglas 2017-01-04 22:43:03 +01:00 committed by Fabien Potencier
parent c3a50b0931
commit 4125455775
2 changed files with 34 additions and 5 deletions

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Serializer\Normalizer;
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
@ -260,6 +261,16 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
}
}
// 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.
// To circumvent this behavior, integers are converted to floats when denormalizing JSON based formats and when
// a float is expected.
if (Type::BUILTIN_TYPE_FLOAT === $builtinType && is_int($data) && false !== strpos($format, JsonEncoder::FORMAT)) {
return (float) $data;
}
if (call_user_func('is_'.$builtinType, $data)) {
return $data;
}

View File

@ -537,11 +537,21 @@ class ObjectNormalizerTest extends \PHPUnit_Framework_TestCase
'inners' => array(array('foo' => 1), array('foo' => 2)),
), ObjectOuter::class);
$this->assertEquals('foo', $obj->getInner()->foo);
$this->assertEquals('bar', $obj->getInner()->bar);
$this->assertEquals('1988-01-21', $obj->getDate()->format('Y-m-d'));
$this->assertEquals(1, $obj->getInners()[0]->foo);
$this->assertEquals(2, $obj->getInners()[1]->foo);
$this->assertSame('foo', $obj->getInner()->foo);
$this->assertSame('bar', $obj->getInner()->bar);
$this->assertSame('1988-01-21', $obj->getDate()->format('Y-m-d'));
$this->assertSame(1, $obj->getInners()[0]->foo);
$this->assertSame(2, $obj->getInners()[1]->foo);
}
public function testAcceptJsonNumber()
{
$extractor = new PropertyInfoExtractor(array(), array(new PhpDocExtractor(), new ReflectionExtractor()));
$normalizer = new ObjectNormalizer(null, null, null, $extractor);
$serializer = new Serializer(array(new ArrayDenormalizer(), new DateTimeNormalizer(), $normalizer));
$this->assertSame(10.0, $serializer->denormalize(array('number' => 10), JsonNumber::class, 'json')->number);
$this->assertSame(10.0, $serializer->denormalize(array('number' => 10), JsonNumber::class, 'jsonld')->number);
}
/**
@ -820,3 +830,11 @@ class FormatAndContextAwareNormalizer extends ObjectNormalizer
return false;
}
}
class JsonNumber
{
/**
* @var float
*/
public $number;
}