简体   繁体   中英

Symfony3: XML deserialize array

The Symfony 3.4 documentation states the following for deserializing arrays:

use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Serializer\Serializer;

$serializer = new Serializer(
array(new GetSetMethodNormalizer(), new ArrayDenormalizer()),
array(new JsonEncoder())
);

$data = ...; // The serialized data from the previous example
$persons = $serializer->deserialize($data, 'Acme\Person[]', 'json');

The json string is the following:

[{"name":"foo","age":99,"sportsman":false},{"name":"bar","age":33,"sportsman":true}]

So I tried to do the same with my XML structure. It's not a real structure as I'm testing the thing.

XML Structure:

<<<EOF
<response>
 <book>
  <titulo>foo</titulo>
  <isbn>99</isbn>
  <autor>Autor</autor>
  <editor>Editor</editor>
 </book>
 <book>
  <titulo>foo2</titulo>
  <isbn>100</isbn>
  <autor>Autor2</autor>
  <editor>Editor2</editor>
 </book>
</response>
EOF;

Response is the default root node name. I have a Book entity with the fields defined identically. I try to deserialize like that:

use AppBundle\Entity\Book;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;

$encoders = array(new XmlEncoder(), new JsonEncoder());
$normalizers = array(new GetSetMethodNormalizer(), new ArrayDenormalizer());
$serializer = new Serializer($normalizers, $encoders);

 $serializer->deserialize($data,  'AppBundle\Entity\Book[]', 'xml');

When I do a var_dump of the deserialize variable, the output is the following:

array(1) { ["book"]=> object(AppBundle\\Entity\\Book)#385 (11) { ["isbn":protected]=> NULL ["autor":protected]=> NULL ["titulo":protected]=> NULL ["fecha_ini":protected]=> NULL ["fecha_fin":protected]=> NULL ["editor":protected]=> NULL ["imgUrl":protected]=> NULL ["cod_autor":protected]=> NULL ["cod_editorial":protected]=> NULL ["cod_coleccion":protected]=> NULL ["cod_mat":protected]=> NULL } }

Data are not recognized and the array has only one element when I expect 2 elements.

Does someone experience something like that? Can you help me where to look for the solution ?

Thanks in advance.

XML is not JSON and the root element is not "just a wrapper for an array", so you have to pay it a due respect. There are two ways to approach this:

1. Introduce a deserialization model for the root element - something like

class Response
{
    /**
     * @var Book[]
     */
    protected $book;

    /**
     * @return Book[]
     */
    public function getBook(): array
    {
        return $this->book;
    }

    /**
     * @param Book[] $book
     */
    public function setBook(array $book): void
    {
        $this->book = $book;
    }
}

and then access books like

$response = $serializer->deserialize($xml,  'App\Entity\Response', 'xml');
$books = $response->getBook();

In this case, however, your simple serializer configuration won't suffice - in order to get books properly deserialized as Book instances it is necessary to add an additional bit of functionality to extract type information for the nested entities :

$encoders = array(new XmlEncoder());
$normalizers = array(new GetSetMethodNormalizer(null, null, new PhpDocExtractor()), new ArrayDenormalizer()); // <- PhpDocExtractor
$serializer = new Serializer($normalizers, $encoders);

PhpDocExtractor extracts type information from PhpDoc comments.

2. Introduce a custom denormalizer

Alternatively you can hook directly into the deserialization process with a custom denormalizer

use App\Entity\Book;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;

class BookArrayDenormalizer extends ArrayDenormalizer
{
    public function supportsDenormalization($data, $type, $format = null, array $context = [])
    {
        // only support deserialization of Book[]
        return Book::class.'[]' === $type;
    }

    public function denormalize($data, $class, $format = null, array $context = [])
    {
        return parent::denormalize(
            $data['book'], // this is the magic to ignore the root element
            $class, $format, $context
        );
    }
}

And enjoy deserializing an array of Books without a wrapping object:

$normalizers = array(new GetSetMethodNormalizer(), new BookArrayDenormalizer(), new ArrayDenormalizer()); // add the new denormalizer
$encoders = array(new XmlEncoder());
$serializer = new Serializer($normalizers, $encoders);

$books = $serializer->deserialize($xml,  'App\Entity\Book[]', 'xml');

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM