Десериализовать XML в Entity с помощью вложенных узлов с помощью JMS Serializer

Я хочу десериализовать XML-файл в объект с помощью JMS Serializer. Это работает очень хорошо для прямых свойств. Но когда дело доходит до вложенных свойств, я не могу заставить их работать без создания связанных сущностей. Например :

<idt>
    <rcs>XXXXXXX</rcs>
    <name>NAME</name>
    <main>
        <adr_1>
            <type>YYYYY</type>
            <street>YYYYYYY</street>
            <zip>XXXXX</zip>
        </adr_1>
    </main>
</idt>

Мне нужно создать сущность Idt, и десериализация будет работать нормально для rcs и name, но для main мне нужно создать сущность Main с отношением OneToOne, которое содержит сущность Adr1, содержащую свойства type, street и zip. Это довольно тяжело. Есть ли способ сообщить сериализатору путь к гидратации свойства? Что-то вроде:

class XmlRawExecutive
{
    /**
     * @var integer
     *
     * @ORM\Column(name="rcs", type="string", length=3, nullable=false)
     * @JMS\Type("string")
     */
    private $rcs;

    /**
     * @var integer
     *
     * @ORM\Column(name="main_adr1_street", type="integer", nullable=false)
     * @JMS\Type("string")
     */
    private $mainAdr1Street;

Таким образом, я могу гидратировать уникальный объект из XML.


person Hakim    schedule 11.05.2018    source источник


Ответы (1)


JMS Serializer предъявляет довольно строгие требования к тому, как десериализованные данные сопоставляются с результирующими объектами. Я бы предложил использовать более гибкую библиотеку для подобных сценариев.

Однако мне удалось десериализовать данный XML в заданный плоский объект даже с помощью JMS Serializer. Решение использует pre_deserialize прослушиватель событий, изменяющий проанализированные XML-данные:

use JMS\Serializer\EventDispatcher\EventDispatcher;
use JMS\Serializer\EventDispatcher\PreDeserializeEvent;
use JMS\Serializer\SerializerBuilder;
use JMS\Serializer\Annotation as JMS;

class Idt
{
    /**
     * @JMS\Type("string")
     */
    protected $rcs;

    /**
     * @JMS\Type("string")
     */
    protected $mainAdr1Street;
}

/**
 * turns nested elements into children of the root element
 * with names combining names of all the ancestors
 */
function recursiveChildrenInliner (\SimpleXMLElement $root, \SimpleXMLElement $child, $stack = []) {
    if ($child->count() === 0) {
        // produces element name like 'main_adr1_street'
        $name = join('_', array_map(
            function ($n) { return str_replace('_', '', $n); },
            $stack
        ));
        $root->addChild($name, $child->__toString());
    } else {
        foreach ($child->children() as $child) {
            $stackCopy = $stack;
            array_push($stackCopy, $child->getName());

            recursiveChildrenInliner(
                $root,
                $child,
                $stackCopy
            );
        }
    }
};

// build custom serializer instance with a listener registered
$serializer = SerializerBuilder::create()
    ->configureListeners(function (EventDispatcher $dispatcher) {
        $dispatcher->addListener('serializer.pre_deserialize',
            function (PreDeserializeEvent $event) {
                $data = $event->getData();

                recursiveChildrenInliner($data, $data->main, ['main']);
            }
        );
    })
    ->build();

$result = $serializer->deserialize($xml, Idt::class, 'xml');
person igneus    schedule 09.08.2018
comment
Спасибо за ответ, хотя мне пришлось пойти с подсущностями (у проекта был короткий срок). Если у меня есть время, я мог бы попытаться интегрировать это! - person Hakim; 09.08.2018