Symfony - Deserialize json to an array of entities
Asked Answered
B

6

24

I have a json object that I received by making a get API call. I make this call to receive a list of objects. It's a list of post... So I have an array of Post Objects.

Here the output :

{
    "total":2,
    "data":[
      {
        "id":2,
        "user":{
          "id":1,
          "username":"sandro.tchikovani"             
        },
        "description":"cool",
        "nb_comments":0,
        "nb_likes":0,
        "date_creation":"2014-04-13T20:07:34-0700"
      },
      {
        "id":1,
        "user":{
           "id":1,
           "username":"sandro.tchikovani",
         },
        "description":"Premier pooooste #lol",
        "nb_comments":0,
        "nb_likes":0,
        "date_creation":"2014-04-13T15:15:35-0700"
      }
    ]
 }

I would like to deserialize the data part... The problem is that the Serializer in Symfony gives me an error ...

The error that I have :

Class array<Moodress\Bundle\PosteBundle\Entity\Poste> does not exist

How I do deserialize :

$lastPosts = $serializer->deserialize($data['data'], 'array<Moodress\Bundle\PosteBundle\Entity\Poste>', 'json');

How can I deserialze the data array... To have an array of Postes. I want to give to my view .twig an array Poste... I did precise the type when I deserialize... So I can't find what is the problem...

Thanks.

Birdcage answered 14/4, 2014 at 3:38 Comment(2)
Did you get any further with your solution? I'm wondering how to get JMS to match such a data object within the json.Dougie
I didn't have choice... I just made a for each on the array, and deserialize the data for each value... I would have preferred to it another way, but I couldn't find any other solution.Birdcage
E
9

The error is pretty clear. Your string does not match any existant class.

The example in official documentation says:

$person = $serializer->deserialize($data,'Acme\Person','xml');

In your case it should be more like:

$person = $serializer->deserialize($data['data'],'Moodress\Bundle\PosteBundle\Entity\Poste','json');

Update:

Ok then.

First, your json file does not seem to be valid (use http://jsonlint.com/ to test it). Be careful of that.

Second, you will have to fetch your json as an array with

$data = json_decode($yourJsonFile, true);

and then you can access to each 'data' array with

foreach($data['data'] as $result)
{
    /* Here you can hydrate your object manually like:
    $person = new Person();
    $person->setId($user['id']);
    $person->setDescription($user['description']);

    Or you can use a denormalizer. */
}
Earth answered 14/4, 2014 at 5:39 Comment(2)
The problem is that $data['data'] is an array of Postes... Not just one... So I have to make understand to serializer that I want to deserialize an array of my entity type. Do you know what I mean?Birdcage
Yeah, I thought about that... As ou said, it does the trick, but in the documentation it says that it's possible to serialize directly the array... If I don't find any other solution, I will do it by step like you did demonstrate. I just think it would be better to use the array type... But I don't know why, JMSSerialize doesn't like my code.Birdcage
L
26

Since Symfony Serializer Component 2.8 to deserialize array of objects:

$persons = $serializer->deserialize($data, 'Acme\Person[]', 'json');

https://symfony.com/doc/master/components/serializer.html#handling-arrays

Limousine answered 30/1, 2018 at 23:39 Comment(0)
P
24

I think the best solution here is to create new PosteResponse class, like this one:

namespace Moodress\Bundle\PosteBundle\Response;

use JMS\Serializer\Annotation\Type;

class PosteResponse
{
    /**
     * @Type("integer")
     */
    private $total;

    /**
     * @Type("array<Moodress\Bundle\PosteBundle\Entity\Poste>")
     */
    private $data;

    //getters here
}

and deserialize your response to that class:

$response = $serializer->deserialize(
    $json,
    'Moodress\Bundle\PosteBundle\Response\PosteResponse',
    'json'
);
$posts = $response->getData();

That WILL do the trick, and it doesn't require you to decode and encode your json manually which is riddiculous in my opinion.

Paralyse answered 5/5, 2015 at 11:42 Comment(4)
OP specifically asked about the Symfony Serializer not JMS Serializer.Damnify
@MikeDoe the same solution applies also to Symfony Serializer, just annotations are different.Paralyse
Nope. There's no Type annotation in Symfony's serializer.Damnify
That's why I said "annotations are different". Still creating a different class for the response as a whole applies to Symfony Serializer as well as any other class-based serializer you can think of.Paralyse
D
13

A less than ideal solution that I found was to first decode and then encode the json data again at the node that represents the data array. For example in your case:

$json = json_decode($json);
$json = json_encode($json->data);
$serializer->deserialize($json, 'array<Moodress\Bundle\PosteBundle\Entity\Poste>', 'json');

There must be a better solution than this but this seems more elegant than the above solution of de-serialising json.

Dougie answered 24/6, 2014 at 8:4 Comment(1)
Exactly what I needed. This should be the accepted answer.Nose
E
9

The error is pretty clear. Your string does not match any existant class.

The example in official documentation says:

$person = $serializer->deserialize($data,'Acme\Person','xml');

In your case it should be more like:

$person = $serializer->deserialize($data['data'],'Moodress\Bundle\PosteBundle\Entity\Poste','json');

Update:

Ok then.

First, your json file does not seem to be valid (use http://jsonlint.com/ to test it). Be careful of that.

Second, you will have to fetch your json as an array with

$data = json_decode($yourJsonFile, true);

and then you can access to each 'data' array with

foreach($data['data'] as $result)
{
    /* Here you can hydrate your object manually like:
    $person = new Person();
    $person->setId($user['id']);
    $person->setDescription($user['description']);

    Or you can use a denormalizer. */
}
Earth answered 14/4, 2014 at 5:39 Comment(2)
The problem is that $data['data'] is an array of Postes... Not just one... So I have to make understand to serializer that I want to deserialize an array of my entity type. Do you know what I mean?Birdcage
Yeah, I thought about that... As ou said, it does the trick, but in the documentation it says that it's possible to serialize directly the array... If I don't find any other solution, I will do it by step like you did demonstrate. I just think it would be better to use the array type... But I don't know why, JMSSerialize doesn't like my code.Birdcage
N
2

I would make something like this

class PostsModel
{
    /**
     * @var int
     */
    private $total;

    /**
     * @var PostModel[]
     */
    private $data;
}

class PostModel
{
    /**
     * @var int
     */
    private $id;

    /**
     * @var UserModel
     */
    private $user;

    /**
     * @var string
     */
    private $description;

    /**
     * @var  int
     */
    private $nb_comments;

    /**
     * @var int
     */
    private $nb_likes;

    /**
     * @var \DateTime
     */
    private $date_creation;
}

class UserModel
{
    /**
     * @var int
     */
    private $id;

    /**
     * @var string
     */
    private $username;
}

And in controller

            $posts = $this->serializer->deserialize($data, PostsModel::class, 'json');

And this will return $postsModel with $data property which will have your array of entities

Nibble answered 26/6, 2018 at 12:9 Comment(0)
H
1

In case someone will be searching how to decode an array of objects using Symfony Serializer:

use Moodress\Bundle\PosteBundle\Entity\Poste;

// your json data 
$data = '{
  "total":2,
  "data":[
    {...},
    {...}
  ]
}';

$lastPosts = $serializer->deserialize(
    $data['data'],
    'Moodress\Bundle\PosteBundle\Entity\Poste[]', // or Poste::class.'[]',
    'json'
);

Notice that you need to add [] after your class name, in this way Symfony will recognize your json data as an array of objects.

Heaviside answered 26/7, 2021 at 18:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.