JMSSerializer:将空DateTime XML元素反序列化为PHP "null"对象


JMSSerializer: Deserializing empty DateTime XML element into PHP "null" object

我正在处理XML文件的反序列化。有些元素可能不包含任何数据,所以我试图将以下XML元素(OfferDate)反序列化为null对象,而不是'DateTime对象:

<Product>
    <OfferDate></OfferDate>
</Product>

…但我得到以下错误:

JMS'Serializer'Exception'RuntimeException: Invalid datetime "", expected format Y-m-d'TH:i:s.
./vendor/jms/serializer/src/JMS/Serializer/Handler/DateHandler.php:117
./vendor/jms/serializer/src/JMS/Serializer/Handler/DateHandler.php:99
./vendor/jms/serializer/src/JMS/Serializer/GraphNavigator.php:180
./vendor/jms/serializer/src/JMS/Serializer/XmlDeserializationVisitor.php:280
./vendor/jms/serializer/src/JMS/Serializer/GraphNavigator.php:236
./vendor/jms/serializer/src/JMS/Serializer/XmlDeserializationVisitor.php:175
./vendor/jms/serializer/src/JMS/Serializer/GraphNavigator.php:130
./vendor/jms/serializer/src/JMS/Serializer/XmlDeserializationVisitor.php:251
./vendor/jms/serializer/src/JMS/Serializer/GraphNavigator.php:236
./vendor/jms/serializer/src/JMS/Serializer/Serializer.php:182
./vendor/jms/serializer/src/JMS/Serializer/Serializer.php:116
./vendor/phpoption/phpoption/src/PhpOption/Some.php:89
./vendor/jms/serializer/src/JMS/Serializer/Serializer.php:119
./tests/AppBundle/Domain/Model/ProductTest.php:35
./tests/AppBundle/Domain/Model/ProductTest.php:44

如果XML文件将包含例如OfferDate中的2016-09-25T18:58:55,它将工作,因为有一些数据…但由于也有可能存在没有任何数据的元素,所以我也必须涉及这种情况。

将XML反序列化为对象的YML映射:

AppBundle'Domain'Model'Product:
  xml_root_name: Product
  properties:
    offerDate:
      serialized_name: OfferDate
      type: DateTime<'Y-m-d'TH:i:s'>

My Product class:

<?php
declare(strict_types = 1);
namespace AppBundle'Domain'Model;
/**
 * @author ...
 */
class Product
{
    /**
     * @var 'DateTime
     */
    private $offerDate;
    /**
     * @return 'DateTime
     */
    public function getOfferDate(): 'DateTime
    {
        return $this->offerDate;
    }
}

最后是反序列化:

$xml = file_get_contents(__DIR__.'/product.xml');
$serializer = SerializerBuilder::create()
                               ->addMetadataDir(__DIR__.'/../../../../app/config/serializer')
                               ->build();
/** @var ProductCollection $productCollection */
$productCollection  = $serializer->deserialize($xml, ProductCollection::class, 'xml');
$firstProduct = $productCollection->getProducts()[0];
var_dump($firstProduct->getOfferDate());

./tests/AppBundle/Domain/Model/ProductTest.php:35,如上图所示,在错误中等于$productCollection = $serializer->deserialize($xml, ProductCollection::class, 'xml');行。

澄清为什么我反序列化成一个ProductCollection:product.xml包含一个<Products>元素,其中包含<Product>元素。然后ProductCollection包含一个名为getProducts()的方法,该方法返回一个包含反序列化Product对象的数组。

是否有一种方法来反序列化OfferDate元素,没有任何数据,到null对象?如果有,是怎么做到的?

我已经为DateTime对象的反序列化过程创建了一个Handler。

这是我的解决方案。我的DateTimeHandler覆盖了JMSSerializer提供的默认DateHandler类和方法deserializeDateTimeFromXml:

<?php
declare(strict_types = 1);
namespace AppBundle'Serializer'Handler;
use JMS'Serializer'Handler'DateHandler;
use JMS'Serializer'XmlDeserializationVisitor;
/**
 * @author ...
 */
class DateTimeHandler extends DateHandler
{
    /**
     * @param XmlDeserializationVisitor $visitor
     * @param $data
     * @param array $type
     *
     * @return 'DateTime|null
     */
    public function deserializeDateTimeFromXml(XmlDeserializationVisitor $visitor, $data, array $type)
    {
        // Casting the data to a string will return the value of the
        // current xml element. So if it's empty there is no data.
        if ((string)$data === '') {
            return null;
        }
        return parent::deserializeDateTimeFromXml($visitor, $data, $type);
    }
}

然后在反序列化中:(注意configureHandlers方法)

$xml = file_get_contents(__DIR__.'/product.xml');
$serializer = SerializerBuilder::create()
                               ->addMetadataDir(__DIR__.'/../../../../app/config/serializer')
                               ->configureHandlers(
                                   function (HandlerRegistry $registry) {
                                       $registry->registerSubscribingHandler(new DateTimeHandler());
                                   }
                               )
                               ->build();
/** @var ProductCollection $productCollection */
$productCollection = $serializer->deserialize($xml, ProductCollection::class, 'xml');

这现在工作得很好!