Deserialize XML to Entity with nested nodes using JMS Serializer

穿精又带淫゛_ 提交于 2019-12-24 00:48:07

问题


I want to deserialize an XML file into an Entity using JMS Serializer. It works pretty well for direct properties. But when it comes to nested properties, I can't make it work without creating related entities. For example :

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

I need to create an Idt entity, and the deserialization will work fine for rcs and name, but for main I have to create a Main entity with a OneToOne relation that contains a Adr1 entity that contains type, street and zip properties. This is pretty heavy. Is there any way to tell the serializer the path to hydrate a property? Something of the like :

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;

So I can hydrate a unique entity from the XML.


回答1:


JMS Serializer imposes quite strict expectations on the way how deserialized data are mapped to the resulting objects. I would suggest using some more flexible library for scenarios like this.

However, I managed to deserialize the given XML to the given flat object even with JMS Serializer. The solution uses a pre_deserialize event listener, modifying the parsed XML data:

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


来源:https://stackoverflow.com/questions/50294500/deserialize-xml-to-entity-with-nested-nodes-using-jms-serializer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!