Symfony3: массив десериализации XML

В документации Symfony 3.4 говорится следующее о десериализации массивов:

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

Строка json следующая:

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

Поэтому я попытался сделать то же самое со своей XML-структурой. Это не настоящая структура, поскольку я ее тестирую.

Структура XML:

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

Ответ - это имя корневого узла по умолчанию. У меня есть сущность Book с идентично определенными полями. Я пытаюсь десериализовать вот так:

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

Когда я делаю var_dump переменной deserialize, результат будет следующим:

array (1) {["book"] => объект (AppBundle \ Entity \ Book) # 385 (11) {["isbn": protected] => NULL ["автор": 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}}

Данные не распознаются, и в массиве только один элемент, когда я ожидаю 2 элемента.

Кто-нибудь испытывает что-то подобное? Вы можете мне помочь, где искать решение?

Заранее спасибо.


person Appyapp    schedule 02.03.2018    source источник
comment
можешь поделиться $data? и попробуйте удалить <response>?   -  person Joseph D.    schedule 02.03.2018
comment
$data - это структура XML выше. Если я удалю <response>, мне придется заменить его другим корневым узлом. Который из? Без него структура xml не может быть действительной.   -  person Appyapp    schedule 03.03.2018


Ответы (1)


XML - это не JSON, а корневой элемент - это не «просто оболочка для массива», поэтому вы должны проявить к нему должное уважение. Есть два подхода к этому:

1. Представьте модель десериализации для корневого элемента - что-то вроде

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;
    }
}

а затем получить доступ к таким книгам, как

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

В этом случае, однако, вашей простой конфигурации сериализатора будет недостаточно - для правильной десериализации книг в виде Book экземпляров необходимо добавить дополнительную функциональность в извлечь информацию о типе вложенных объектов:

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

PhpDocExtractor извлекает информацию о типе из комментариев PhpDoc.

2. Представьте собственный денормализатор

В качестве альтернативы вы можете напрямую подключиться к процессу десериализации с помощью настраиваемого денормализатора

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

И наслаждайтесь десериализацией массива книг без объекта-оболочки:

$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');
person igneus    schedule 12.04.2019