Por Jean Hertel, 06/04/2017
Hoje tive que transformar um JSON complexo em objetos e sofri bastante para descobrir a solução. O JSON em questão era algo parecido com isto:
{
"name": "author name",
"posts": [
{
"title": "some post 1"
},
{
"title": "some post 2"
}
]
}
As classes por sua vez:
<?php
class Author
{
private $name;
private $posts;
public function __construct()
{
$this->posts = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getPosts()
{
return $this->posts;
}
public function setPosts(array $posts)
{
$this->posts->clear();
foreach ($posts as $post) {
$this->addPosts($post);
}
}
public function addPosts(Post $post)
{
$this->posts->add($post);
}
public function removePosts(Post $post)
{
$this->posts->removeElement($post);
}
}
class Post
{
private $title;
public function getTitle()
{
return $this->title;
}
public function setTitle($title)
{
$this->title = $title;
}
}
Mas como fazemos isso? Se você ainda não leu meu post sobre como configurar o serializer, leia aqui.
Se você configurou assim como eu fiz no outro post, apenas será utilizado get e set para carregar as propriedades das classes.
Para ser possível deserializar arrays é preciso configurar um Symfony\Component\Serializer\Normalizer\ArrayDenormalizer
.
Mas só isso não é o bastante, pois a coleção vai ser deserializada e repassada como um array.
Para detectar corretamente o nosso objeto, precisamos informar ao serializador o seu tipo.
Isto pode ser feito configurando um Symfony\Component\Serializer\Normalizer\ObjectNormalizer
com um Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor
.
Um jeito rápido de fazer isso é pegar o exemplo da outra página e alterar para ficar desta forma:
<?php
$app['serializer.normalizers'] = function () use ($app) {
$propertyInfo = new \Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor();
return [
new \Symfony\Component\Serializer\Normalizer\ArrayDenormalizer(),
new \Symfony\Component\Serializer\Normalizer\ObjectNormalizer(null, null, null, $propertyInfo)
];
};
A classe ObjectNormalizer
lê as propriedades dos objetos e chama os seus getters e setters.
Com o auxilio da classe ReflectionExtractor
ela consegue saber os tipos de objetos.
Mas como os tipos são descobertos? O importante aqui é ter os métodos addPosts
e removePosts
recebendo como parâmetro o objeto com o seu type hint correto. Desta forma o extrator consegue identificar
qual objeto deve deserializar.
Um exemplo completo e funcional está disponivel neste repositório.
Um último detalhe: a classe ReflectionExtractor
necessita das dependencias symfony/property-info
e symfony/property-access
.