0

Я работаю над десериализацией XML-файла. Вполне возможно, что некоторые элементы не будут содержать никаких данных, так что я пытаюсь десериализации следующий XML-элемент (OfferDate) в null объекта вместо \DateTime объекта:JMSSerializer: десериализация пустой XML-элемент DateTime в PHP «нулевой» объект

<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 будет содержать, например, 2016-09-25T18:58:55 в OfferDate он будет работать, так как есть некоторые данные ... Но так как это также возможно, что там могут быть элементы без каких-либо данных, я должен и этот случай тоже.

Мои YML отображение десериализации XML в объект:

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

Мой Product класс:

И, наконец, моя десериализации:

$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');.

To clarify why I deserialize into a ProductCollection : The product.xml contains a <Products> element in which <Product> elements are. The ProductCollection then contains a method called getProducts() which returns an array containing the deserialized Product objects.

Есть ли способ десериализации OfferDate элемента, без каких-либо данных, в null объект? И если да, то как?

ответ

0

Я придумал создание Handler для процесса десериализации объектов DateTime.

Вот мое решение. Мои DateTimeHandler перекрывая по умолчанию DateHandler ВЛЯЕТСЯ класс и метода deserializeDateTimeFromXml предоставленного JMSSerializer:

<?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'); 

Теперь это работает прекрасно!