3

I would like to convert a json into a object / model. If the json is only one-dimensional, it works perfectly.

But if it is multidimensional, only the outer (user) is converted, but not the inner (company), this remains an array. Can you help me with this?

The Models:

<?php
namespace AppBundle;

class Company {
    /**
     * @var string
     */
    protected $companyName = '';

    /**
     * @return string
     */
    public function getCompanyName()
    {
        return $this->companyName;
    }

    /**
     * @param string $companyName
     * @return void
     */
    public function setCompanyName($companyName)
    {
        $this->companyName = $companyName;
    }
}

class User {
    /**
     * @var \AppBundle\Company
     */
    protected $company = null;

    /**
     * @var string
     */
    protected $username = '';

    /**
     * @return \AppBundle\Company
     */
    public function getCompany() {
        return $this->company;
    }

    /**
     * @param \AppBundle\Company $company
     * @return void
     */
    public function setCompany($company) {
        $this->company = $company;
    }

    /**
     * @return string
     */
    public function getUsername() {
        return $this->username;
    }

    /**
     * @param string $username
     * @return void
     */
    public function setUsername($username) {
        $this->username = $username;
    }
}
?>

Convert json to model:

<?php
namespace AppBundle\Controller;

class DefaultController extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{

    public function indexAction()
    {
        // Initialize serializer
        $objectNormalizer = new \Symfony\Component\Serializer\Normalizer\ObjectNormalizer();
        $jsonEncoder = new \Symfony\Component\Serializer\Encoder\JsonEncoder();
        $serializer = new \Symfony\Component\Serializer\Serializer([$objectNormalizer], [$jsonEncoder]);

        // Set test model
        $company = new \AppBundle\Company();
        $company->setCompanyName('MyCompany');
        $user = new \AppBundle\User();
        $user->setCompany($company);
        $user->setUsername('MyUsername');

        // Serialize test model to json
        $json = $serializer->serialize($user, 'json');
        dump($user); // Model ok, Company is instance of \AppBundle\Company
        dump($json); // json ok + valide

        // Deserialize json to model
        $user = $serializer->deserialize($json, \AppBundle\User::class, 'json');
        dump($user); // Error: Company is now array instead instance of \AppBundle\Company

        // Denormalize json to model
        $userArray = $serializer->decode($json, 'json');
        $user = $serializer->denormalize($userArray, \AppBundle\User::class);
        dump($user); // Error: Company is now array instead instance of \AppBundle\Company
    }
}
?>

1 Answer 1

6

I solved the problem.

On the one hand you need PHP 7. Annotations I have not yet tested.

Then the variable has to be set correctly in setCompany().

public function setCompany(Company $company) {
    $this->company = $company;
}

And the ReflectionExtractor() must be used.

use Symfony\Component\Serializer\Normalizer;
use Symfony\Component\PropertyInfo\Extractor;
$objectNormalizer = new ObjectNormalizer(
    null,
    null,
    null,
    new ReflectionExtractor()
);

You only need deserialize(), because it decode() and denormalize(). http://symfony.com/doc/current/components/serializer.html

Full fixed code:

Company class:

class Company {
    /**
     * @var string
     */
    protected $companyName = '';

    /**
     * @return string
     */
    public function getCompanyName() {
        return $this->companyName;
    }

    /**
     * @param string $companyName
     * @return void
     */
    public function setCompanyName($companyName) {
        $this->companyName = $companyName;
    }
}

User class:

class User {
    /**
     * @var \AppBundle\Company
     */
    protected $company = null;

    /**
     * @var string
     */
    protected $username = '';

    /**
     * @return \AppBundle\Company
     */
    public function getCompany() {
        return $this->company;
    }

    /**
     * @param \AppBundle\Company $company
     * @return void
     */
    public function setCompany(Company $company) {
        $this->company = $company;
    }

    /**
     * @return string
     */
    public function getUsername() {
        return $this->username;
    }

    /**
     * @param string $username
     * @return void
     */
    public function setUsername($username) {
        $this->username = $username;
    }
}
?>

Controller class:

<?php

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller;
use Symfony\Component\Serializer\Normalizer;
use Symfony\Component\PropertyInfo\Extractor;
use Symfony\Component\Serializer;

class DefaultController extends Controller {
    public function indexAction() {
        $objectNormalizer = new ObjectNormalizer(
            null,
            null,
            null,
            new ReflectionExtractor()
        );
        $jsonEncoder = new JsonEncoder();
        $serializer = new Serializer([$objectNormalizer], [$jsonEncoder]);

        $company = new \AppBundle\Company();
        $company->setCompanyName('MyCompany');

        $user = new \AppBundle\User();
        $user->setCompany($company);
        $user->setUsername('MyUsername');

        $json = $serializer->serialize($user, 'json');
        dump($user, $json);

        $user2 = $serializer->deserialize($json, \AppBundle\User::class, 'json');
        dump($user2);
    }
}
?>
Sign up to request clarification or add additional context in comments.

2 Comments

Nice one. I also found Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor, which read the PHP Doc (@var) to get the type, so you can use DTO with public properties, without mandatory typed getter/setter.
Finally an answer that works! Thanks. This area of Symfony is so convoluted it's impossible to figure out. Serializers, normalizers, denormilzers, and all entwined.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.