From 1f9021ecb8d536ba7d716faf69369d4fbe3c8a87 Mon Sep 17 00:00:00 2001 From: pbouwdewijn Date: Mon, 22 May 2017 10:50:32 +0200 Subject: [PATCH] Moved isNullData away to a separate `NullEvaluatorInterface` Only the XMLDeserializationVisitor implements the `NullEvaluatorInterface` Restored behaviour in the GraphNavigator for the SerializationContext (elseif branch) Removed now obsolete tests from the BaseSerializationTest --- src/AbstractVisitor.php | 10 ------- src/GraphNavigator.php | 12 ++++++-- src/NullEvaluatorInterface.php | 32 ++++++++++++++++++++++ src/VisitorInterface.php | 10 ------- src/XmlDeserializationVisitor.php | 15 ++++------ tests/Serializer/BaseSerializationTest.php | 13 --------- tests/Serializer/XmlSerializationTest.php | 5 ++-- 7 files changed, 51 insertions(+), 46 deletions(-) create mode 100644 src/NullEvaluatorInterface.php diff --git a/src/AbstractVisitor.php b/src/AbstractVisitor.php index ec469c185..ce18915e1 100644 --- a/src/AbstractVisitor.php +++ b/src/AbstractVisitor.php @@ -62,14 +62,4 @@ protected function getElementType($typeArray) return $typeArray['params'][0]; } } - - /** - * @param mixed $data - * - * @return bool - */ - public function isNullData($data) - { - return $data === null; - } } diff --git a/src/GraphNavigator.php b/src/GraphNavigator.php index bcccb60b6..c1b308d0b 100644 --- a/src/GraphNavigator.php +++ b/src/GraphNavigator.php @@ -119,10 +119,18 @@ public function accept($data, array $type = null, Context $context) $type = array('name' => $typeName, 'params' => array()); } - // If the data is null, we have to force the type to null regardless of the input in order to // guarantee correct handling of null values, and not have any internal auto-casting behavior. - if ($visitor instanceof VisitorInterface && true === $visitor->isNullData($data)) { + else if ($context instanceof SerializationContext && null === $data) { + $type = array('name' => 'NULL', 'params' => array()); + } + + // Sometimes data can convey null but is not of a null type. + // Visitors can have the power to add this custom null evaluation + if ($visitor instanceof VisitorInterface + && $visitor instanceof NullEvaluatorInterface + && $visitor->evaluatesToNull($data) === true + ) { $type = array('name' => 'NULL', 'params' => array()); } diff --git a/src/NullEvaluatorInterface.php b/src/NullEvaluatorInterface.php new file mode 100644 index 000000000..03646c137 --- /dev/null +++ b/src/NullEvaluatorInterface.php @@ -0,0 +1,32 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\Serializer; + +interface NullEvaluatorInterface +{ + /** + * Determine if a value conveys a null value. + * An example could be an xml element (Dom, SimpleXml, ...) that is tagged with a xsi:nil attribute + * + * @param mixed $value + * + * @return bool + */ + public function evaluatesToNull($value); +} diff --git a/src/VisitorInterface.php b/src/VisitorInterface.php index 0728975d8..7b8480047 100644 --- a/src/VisitorInterface.php +++ b/src/VisitorInterface.php @@ -138,14 +138,4 @@ public function getNavigator(); * @return object|array|scalar */ public function getResult(); - - /** - * Determine if the value evaluates to null. - * Used by the navigator to determine the correct data type. - * - * @param mixed $data - * - * @return bool - */ - public function isNullData($data); } diff --git a/src/XmlDeserializationVisitor.php b/src/XmlDeserializationVisitor.php index 904bc2322..bcf5a08b0 100644 --- a/src/XmlDeserializationVisitor.php +++ b/src/XmlDeserializationVisitor.php @@ -25,7 +25,7 @@ use JMS\Serializer\Metadata\ClassMetadata; use JMS\Serializer\Metadata\PropertyMetadata; -class XmlDeserializationVisitor extends AbstractVisitor +class XmlDeserializationVisitor extends AbstractVisitor implements NullEvaluatorInterface { private $objectStack; private $metadataStack; @@ -389,17 +389,14 @@ private function getDomDocumentTypeEntitySubset($data) } /** - * Consider xml element value null if the xsi:nil attribute is set and therefore can't have a value - * @see https://www.w3.org/TR/xmlschema-1/#xsi_nil - * - * @param $data + * @param mixed $value * * @return bool */ - public function isNullData($data) + public function evaluatesToNull($value) { - if ($data instanceof \SimpleXMLElement) { - $xsiAttributes = $data->attributes('http://www.w3.org/2001/XMLSchema-instance'); + if ($value instanceof \SimpleXMLElement) { + $xsiAttributes = $value->attributes('http://www.w3.org/2001/XMLSchema-instance'); //We have to keep the isset quiet, some tests give error: `Node no longer exists`; even though it evaluates to false if (@isset($xsiAttributes['nil']) && (string) $xsiAttributes['nil'] === 'true') { @@ -407,6 +404,6 @@ public function isNullData($data) } } - return $data === null; + return $value === null; } } diff --git a/tests/Serializer/BaseSerializationTest.php b/tests/Serializer/BaseSerializationTest.php index 96301c364..d0e7c71f6 100644 --- a/tests/Serializer/BaseSerializationTest.php +++ b/tests/Serializer/BaseSerializationTest.php @@ -1364,19 +1364,6 @@ public function testObjectWithNullableArrays() $this->assertEquals($this->getContent('nullable_arrays'), $this->serializer->serialize($object, $this->getFormat())); } - public function testIsNullDataWithNull() - { - /** @var VisitorInterface $visitor */ - foreach ($this->serializationVisitors as $visitor) { - $this->assertTrue($visitor->isNullData(null)); - } - - /** @var VisitorInterface $visitor */ - foreach ($this->deserializationVisitors as $visitor) { - $this->assertTrue($visitor->isNullData(null)); - } - } - abstract protected function getContent($key); abstract protected function getFormat(); diff --git a/tests/Serializer/XmlSerializationTest.php b/tests/Serializer/XmlSerializationTest.php index 98dd4dc9d..8d7183fed 100644 --- a/tests/Serializer/XmlSerializationTest.php +++ b/tests/Serializer/XmlSerializationTest.php @@ -490,13 +490,14 @@ public function testDeserializeEmptyString() $this->deserialize('', 'stdClass'); } - public function testIsNullDataWithXSINilLabeledElement() + public function testEvaluatesToNull() { $namingStrategy = $this->getMockBuilder(PropertyNamingStrategyInterface::class)->getMock(); $visitor = new XmlDeserializationVisitor($namingStrategy); $element = simplexml_load_string(''); - $this->assertTrue($visitor->isNullData($element)); + $this->assertTrue($visitor->evaluatesToNull($element)); + $this->assertTrue($visitor->evaluatesToNull(null)); } private function xpathFirstToString(\SimpleXMLElement $xml, $xpath)