From f41060fdb6366fa1549fa8fad912f1f5e298d026 Mon Sep 17 00:00:00 2001 From: jewome62 Date: Mon, 9 Dec 2019 20:35:25 +0100 Subject: [PATCH 1/3] cast numeric into xml node value to php type instead of string --- src/Symfony/Component/Serializer/CHANGELOG.md | 4 ++++ .../Component/Serializer/Encoder/XmlEncoder.php | 8 ++++++++ .../Serializer/Tests/Encoder/XmlEncoderTest.php | 16 ++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index f2171cb5f4a10..7abaed1fc623a 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -1,6 +1,10 @@ CHANGELOG ========= +5.1.0 +----- + * Cast to PHP type numeric value from node for XML + 5.0.0 ----- diff --git a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php index 5620dc5c1e666..1c5b5b8bffa30 100644 --- a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php @@ -326,6 +326,14 @@ private function parseXmlValue(\DOMNode $node, array $context = []) } if (1 === $node->childNodes->length && \in_array($node->firstChild->nodeType, [XML_TEXT_NODE, XML_CDATA_SECTION_NODE])) { + if (false !== $val = filter_var($node->firstChild->nodeValue, FILTER_VALIDATE_INT)) { + return $val; + } + + if (false !== $val = filter_var($node->firstChild->nodeValue, FILTER_VALIDATE_FLOAT)) { + return $val; + } + return $node->firstChild->nodeValue; } diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php index b5e1e00d2063e..47763d46bbd13 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php @@ -439,6 +439,22 @@ public function testDecodeScalarRootAttributes() $this->assertEquals($expected, $this->encoder->decode($source, 'xml')); } + public function testDecodeNumericNodeValue() + { + $source = ''."\n". + '76437963.14'."\n"; + + $expected = [ + 'foo' => 7643796, + 'bar' => 3.14, + ]; + + $result = $this->encoder->decode($source, 'xml'); + $this->assertEquals($expected, $result); + $this->assertIsInt($result['foo']); + $this->assertIsFloat($result['bar']); + } + public function testDecodeRootAttributes() { $source = ''."\n". From 63de18f0e95c65c5811d66e56be29501a753cae1 Mon Sep 17 00:00:00 2001 From: jewome62 Date: Tue, 10 Dec 2019 10:20:06 +0100 Subject: [PATCH 2/3] Add context variable to enable/disable this --- src/Symfony/Component/Serializer/CHANGELOG.md | 2 +- .../Component/Serializer/Encoder/XmlEncoder.php | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index 7abaed1fc623a..dcc90b6c9a333 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -3,7 +3,7 @@ CHANGELOG 5.1.0 ----- - * Cast to PHP type numeric value from node for XML + * Cast to PHP type numeric value from node for XML if `XmlEncoder::TYPE_CAST_NODES` set to true into context 5.0.0 ----- diff --git a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php index 1c5b5b8bffa30..82656566bcffc 100644 --- a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php @@ -52,6 +52,7 @@ class XmlEncoder implements EncoderInterface, DecoderInterface, NormalizationAwa const ROOT_NODE_NAME = 'xml_root_node_name'; const STANDALONE = 'xml_standalone'; const TYPE_CAST_ATTRIBUTES = 'xml_type_cast_attributes'; + const TYPE_CAST_NODES = 'xml_type_cast_nodes'; const VERSION = 'xml_version'; private $defaultContext = [ @@ -62,6 +63,7 @@ class XmlEncoder implements EncoderInterface, DecoderInterface, NormalizationAwa self::REMOVE_EMPTY_TAGS => false, self::ROOT_NODE_NAME => 'response', self::TYPE_CAST_ATTRIBUTES => true, + self::TYPE_CAST_NODES => true, ]; /** @@ -325,16 +327,18 @@ private function parseXmlValue(\DOMNode $node, array $context = []) return $node->nodeValue; } + $typeCastNodes = (bool) ($context[self::TYPE_CAST_NODES] ?? $this->defaultContext[self::TYPE_CAST_NODES]); + if (1 === $node->childNodes->length && \in_array($node->firstChild->nodeType, [XML_TEXT_NODE, XML_CDATA_SECTION_NODE])) { - if (false !== $val = filter_var($node->firstChild->nodeValue, FILTER_VALIDATE_INT)) { - return $val; + if (!is_numeric($node->firstChild->nodeValue) || !$typeCastNodes) { + return $node->firstChild->nodeValue; } - if (false !== $val = filter_var($node->firstChild->nodeValue, FILTER_VALIDATE_FLOAT)) { + if (false !== $val = filter_var($node->firstChild->nodeValue, FILTER_VALIDATE_INT)) { return $val; } - return $node->firstChild->nodeValue; + return (float) $node->firstChild->nodeValue; } $value = []; From 967f0709f09d3e00ae9adf173de7a7afbb70b639 Mon Sep 17 00:00:00 2001 From: jewome62 Date: Wed, 11 Dec 2019 15:55:15 +0100 Subject: [PATCH 3/3] Support cast to null when node is empty --- .../Serializer/Encoder/XmlEncoder.php | 8 ++++++-- .../Tests/Encoder/XmlEncoderTest.php | 20 ++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php index 82656566bcffc..1af8a232f9552 100644 --- a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php @@ -323,12 +323,16 @@ private function parseXmlAttributes(\DOMNode $node, array $context = []): array */ private function parseXmlValue(\DOMNode $node, array $context = []) { + $typeCastNodes = (bool) ($context[self::TYPE_CAST_NODES] ?? $this->defaultContext[self::TYPE_CAST_NODES]); + if (!$node->hasChildNodes()) { + if ($typeCastNodes && '' === $node->nodeValue) { + return null; + } + return $node->nodeValue; } - $typeCastNodes = (bool) ($context[self::TYPE_CAST_NODES] ?? $this->defaultContext[self::TYPE_CAST_NODES]); - if (1 === $node->childNodes->length && \in_array($node->firstChild->nodeType, [XML_TEXT_NODE, XML_CDATA_SECTION_NODE])) { if (!is_numeric($node->firstChild->nodeValue) || !$typeCastNodes) { return $node->firstChild->nodeValue; diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php index 47763d46bbd13..291649899d6a1 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php @@ -301,7 +301,11 @@ public function testNoTypeCastAttribute() XML; - $data = $this->encoder->decode($source, 'xml', ['xml_type_cast_attributes' => false]); + $data = $this->encoder->decode($source, 'xml', [ + XmlEncoder::TYPE_CAST_ATTRIBUTES => false, + XmlEncoder::TYPE_CAST_NODES => false, + ]); + $expected = [ '@a' => '018', '@b' => '-12.11', @@ -455,6 +459,20 @@ public function testDecodeNumericNodeValue() $this->assertIsFloat($result['bar']); } + public function testDecodeEmptyNodeValue() + { + $source = ''."\n". + ''."\n"; + + $expected = [ + 'foo' => null, + ]; + + $result = $this->encoder->decode($source, 'xml'); + $this->assertEquals($expected, $result); + $this->assertNull($result['foo']); + } + public function testDecodeRootAttributes() { $source = ''."\n".