Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e0920a2

Browse filesBrowse files
committed
ScalarDenormalizer handle XML/CSV
1 parent 36f7fab commit e0920a2
Copy full SHA for e0920a2

File tree

2 files changed

+96
-4
lines changed
Filter options

2 files changed

+96
-4
lines changed

‎src/Symfony/Component/Serializer/Normalizer/ScalarDenormalizer.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Normalizer/ScalarDenormalizer.php
+45-4Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,19 @@
1212
namespace Symfony\Component\Serializer\Normalizer;
1313

1414
use Symfony\Component\PropertyInfo\Type;
15+
use Symfony\Component\Serializer\Encoder\CsvEncoder;
1516
use Symfony\Component\Serializer\Encoder\JsonEncoder;
17+
use Symfony\Component\Serializer\Encoder\XmlEncoder;
1618
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
1719
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
1820

1921
final class ScalarDenormalizer implements DenormalizerInterface, CacheableSupportsMethodInterface
2022
{
2123
public const SCALAR_TYPES = [
22-
'int' => true,
23-
'bool' => true,
24-
'float' => true,
25-
'string' => true,
24+
Type::BUILTIN_TYPE_INT => true,
25+
Type::BUILTIN_TYPE_BOOL => true,
26+
Type::BUILTIN_TYPE_FLOAT => true,
27+
Type::BUILTIN_TYPE_STRING => true,
2628
];
2729

2830
/**
@@ -34,6 +36,45 @@ public function denormalize($data, string $type, string $format = null, array $c
3436
throw new InvalidArgumentException(sprintf('Data expected to be of one of the types in "%s" ("%s" given).', implode(', ', array_keys(self::SCALAR_TYPES)), get_debug_type($data)));
3537
}
3638

39+
// In XML and CSV all basic datatypes are represented as strings, it is e.g. not possible to determine,
40+
// if a value is meant to be a string, float, int or a boolean value from the serialized representation.
41+
// That's why we have to transform the values, if one of these non-string basic datatypes is expected.
42+
if (\is_string($data) && (XmlEncoder::FORMAT === $format || CsvEncoder::FORMAT === $format)) {
43+
switch ($type) {
44+
case Type::BUILTIN_TYPE_BOOL:
45+
// according to https://www.w3.org/TR/xmlschema-2/#boolean, valid representations are "false", "true", "0" and "1"
46+
if ('false' === $data || '0' === $data) {
47+
return false;
48+
}
49+
if ('true' === $data || '1' === $data) {
50+
return true;
51+
}
52+
53+
throw new NotNormalizableValueException(sprintf('Data expected to be of type "%s" ("%s" given).', $type, $data));
54+
case Type::BUILTIN_TYPE_INT:
55+
if (ctype_digit($data) || '-' === $data[0] && ctype_digit(substr($data, 1))) {
56+
return (int) $data;
57+
}
58+
59+
throw new NotNormalizableValueException(sprintf('Data expected to be of type "%s" ("%s" given).', $type, $data));
60+
case Type::BUILTIN_TYPE_FLOAT:
61+
if (is_numeric($data)) {
62+
return (float) $data;
63+
}
64+
65+
switch ($data) {
66+
case 'NaN':
67+
return \NAN;
68+
case 'INF':
69+
return \INF;
70+
case '-INF':
71+
return -\INF;
72+
default:
73+
throw new NotNormalizableValueException(sprintf('Data expected to be of type "%s" ("%s" given).', $type, $data));
74+
}
75+
}
76+
}
77+
3778
// JSON only has a Number type corresponding to both int and float PHP types.
3879
// PHP's json_encode, JavaScript's JSON.stringify, Go's json.Marshal as well as most other JSON encoders convert
3980
// floating-point numbers like 12.0 to 12 (the decimal part is dropped when possible).

‎src/Symfony/Component/Serializer/Tests/SerializerTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Tests/SerializerTest.php
+51Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
use Symfony\Component\PropertyAccess\PropertyAccessor;
1717
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
1818
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
19+
use Symfony\Component\Serializer\Encoder\CsvEncoder;
1920
use Symfony\Component\Serializer\Encoder\JsonEncoder;
21+
use Symfony\Component\Serializer\Encoder\XmlEncoder;
2022
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
2123
use Symfony\Component\Serializer\Exception\LogicException;
2224
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
@@ -631,6 +633,7 @@ public function testDeserializeScalarFromJson()
631633
$serializer = new Serializer([new ScalarDenormalizer()], ['json' => new JsonEncoder()]);
632634

633635
$this->assertSame(42, $serializer->deserialize('42', 'int', 'json'));
636+
$this->assertSame(-42, $serializer->deserialize('-42', 'int', 'json'));
634637
$this->assertTrue($serializer->deserialize('true', 'bool', 'json'));
635638
$this->assertSame(3.14, $serializer->deserialize('3.14', 'float', 'json'));
636639
$this->assertSame(3.14, $serializer->deserialize('31.4e-1', 'float', 'json'));
@@ -639,6 +642,54 @@ public function testDeserializeScalarFromJson()
639642
$this->assertSame('@Ca$e%', $serializer->deserialize('"@Ca$e%"', 'string', 'json'));
640643
}
641644

645+
public function testDeserializeScalarFromXml()
646+
{
647+
$serializer = new Serializer([new ScalarDenormalizer()], ['xml' => new XmlEncoder()]);
648+
649+
$deserialize = function ($value, string $type) use ($serializer) {
650+
return $serializer->deserialize("<?xml version='1.0'?>\n<tag>$value</tag>", $type, 'xml');
651+
};
652+
653+
$this->assertSame(42, $deserialize('42', 'int'));
654+
$this->assertSame(-42, $deserialize('-42', 'int'));
655+
$this->assertTrue($deserialize('true', 'bool'));
656+
$this->assertTrue($deserialize('1', 'bool'));
657+
$this->assertFalse($deserialize('false', 'bool'));
658+
$this->assertFalse($deserialize('0', 'bool'));
659+
$this->assertSame(3.14, $deserialize('3.14', 'float'));
660+
$this->assertSame(3.14, $deserialize('31.4e-1', 'float'));
661+
$this->assertSame(3.0, $deserialize('3', 'float')); // '3' === json_encode(3.0)
662+
$this->assertNan($deserialize('NaN', 'float'));
663+
$this->assertSame(\INF, $deserialize('INF', 'float'));
664+
$this->assertSame(-\INF, $deserialize('-INF', 'float'));
665+
$this->assertSame(' spaces ', $deserialize(' spaces ', 'string'));
666+
$this->assertSame('@Ca$e%', $deserialize('@Ca$e%', 'string'));
667+
}
668+
669+
public function testDeserializeScalarFromCsv()
670+
{
671+
$serializer = new Serializer([new ArrayDenormalizer(), new ScalarDenormalizer()], ['csv' => new CsvEncoder()]);
672+
673+
$deserialize = function ($value, string $type) use ($serializer) {
674+
return $serializer->deserialize("0\n$value\n", $type, 'csv', [CsvEncoder::AS_COLLECTION_KEY => false]);
675+
};
676+
677+
$this->assertSame(42, $deserialize('42', 'int[]')[0]);
678+
$this->assertSame(-42, $deserialize('-42', 'int[]')[0]);
679+
$this->assertTrue($deserialize('true', 'bool[]')[0]);
680+
$this->assertTrue($deserialize('1', 'bool[]')[0]);
681+
$this->assertFalse($deserialize('false', 'bool[]')[0]);
682+
$this->assertFalse($deserialize('0', 'bool[]')[0]);
683+
$this->assertSame(3.14, $deserialize('3.14', 'float[]')[0]);
684+
$this->assertSame(3.14, $deserialize('31.4e-1', 'float[]')[0]);
685+
$this->assertSame(3.0, $deserialize('3', 'float[]')[0]); // '3' === json_encode(3.0)
686+
$this->assertNan($deserialize('NaN', 'float[]')[0]);
687+
$this->assertSame(\INF, $deserialize('INF', 'float[]')[0]);
688+
$this->assertSame(-\INF, $deserialize('-INF', 'float[]')[0]);
689+
$this->assertSame(' spaces ', $deserialize(' spaces ', 'string[]')[0]);
690+
$this->assertSame('@Ca$e%', $deserialize('@Ca$e%', 'string[]')[0]);
691+
}
692+
642693
public function testDeserializeLegacyScalarType()
643694
{
644695
$this->expectException(NotNormalizableValueException::class);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.