Search code examples
phpsymfonyphp-7jmsserializerbundlejms-serializer

JMS serializer, deserialization and xpath


So I have this sample XML:

<a>
    <bb>
        <b><c>bc1</c></b>
        <b><c>bc2</c></b>
        <b><c>bc3</c></b>
    </bb>
    <cc>
        <bb>
            <b><c>cbc1</c></b>
            <b><c>cbc2</c></b>
            <b><c>cbc3</c></b>
        </bb>
    </cc>
</a>

And this two entities. Parent entity:

<?php
final class A
{
    /**
     * @JMS\Type("array<B>")
     * @JMS\XmlList(entry="b")
     * @JMS\SerializedName("bb")
     */
    private $bb;
    /**
     * @JMS\Type("array<B>")
     * @JMS\XmlList(entry="b")
     * @JMS\SerializedName("cc/bb")
     */
    private $cc;
}

and child entity:

final class B {
    /**
     * @var string
     *
     * @ORM\Column(type="string", length=24)
     * @JMS\Type("string")
     * @JMS\SerializedName("c")
     */
    private $c;
}

Problem is that after deserialization of my xml

    $object = $this->serializer->deserialize($xml, A::class, 'xml');

i got property bb hydrated as i expect, but property cc is empty.

Question is if there is any way to fill that field without intermediary class/entity?


Solution

  • Yes, it could be done in a way similar to https://stackoverflow.com/a/51766169/2034213, by modifying the parsed XML data before deserialization starts. Here you would use a pre_deserialize listener to move cc/bb to become direct descendant of a, named e.g. ccbb, and change the annotation of $a to @JMS\SerializedName("ccbb")

    There is, however, one painful difference to the other question linked above: while adding a simple element with text content is easy with SimpleXML, moving around a subtree of elements can only be done by re-creating the elements recursively, one by one.