Using a Serializer

We're turning Programmers into JSON by hand inside serializeProgrammer():

158 lines src/AppBundle/Controller/Api/ProgrammerController.php
... lines 1 - 15
class ProgrammerController extends BaseController
... lines 18 - 147
private function serializeProgrammer(Programmer $programmer)
return array(
'nickname' => $programmer->getNickname(),
'avatarNumber' => $programmer->getAvatarNumber(),
'powerLevel' => $programmer->getPowerLevel(),
'tagLine' => $programmer->getTagLine(),

That's pretty ok with just one resource, but this will be a pain when we have a lot more - especially when resources start having relations to other resources. It'll turn into a whole soap opera. To make this way more fun, we'll use a serializer library: code that's really good at turning objects into an array, or JSON or XML.

The one we'll use is called "JMS Serializer" and there's a bundle for it called JMSSerializerBundle. This is a fanstatic library and incredibly powerful. It can get complex in a few cases, but we'll cover those. You should also know that this library is not maintained all that well anymore and you'll see a little bug that we'll have to work around. But it's been around for years, it's really stable and has a lot of users.

Symfony itself ships with a serializer, Symfony 2.7 has a lot of features that JMS Serializer has. There's a push inside Symfony to make it eventually replace JMS Serialize for most use-cases. So, keep an eye on that. Oh, and JMS Serializer is licensed under Apache2, which is a little bit less permissive than MIT, which is Symfony's license. If that worries you, look into it further.

With all that out of the way, let's get to work. Copy the composer require line and paste it into the terminal:

composer require jms/serializer-bundle

While we're waiting, copy the bundle line and add this into our AppKernel:

40 lines app/AppKernel.php
... lines 1 - 5
class AppKernel extends Kernel
public function registerBundles()
$bundles = array(
... lines 11 - 19
new \JMS\SerializerBundle\JMSSerializerBundle(),
... lines 22 - 31
return $bundles;
... lines 34 - 40

This gives us a new service calld jms_serializer, which can turn any object into JSON or XML. Not unlike a Harry Potter wizarding spell.... accio JSON! So in the controller, rename serializeProgrammer to serialize and make the argument $data, so you can pass it anything. And inside, just return $this->container->get('jms_serializer') and call serialize() on that, passing it $data and json:

151 lines src/AppBundle/Controller/Api/ProgrammerController.php
... lines 1 - 15
class ProgrammerController extends BaseController
... lines 18 - 144
private function serialize($data)
return $this->container->get('jms_serializer')
->serialize($data, 'json');

PhpStorm is angry, just because composer hasn't finished downloading yet: we're working ahead.

Find everywhere we used serializeProgrammer() and change those. The only trick is that it's not returning an array anymore, it's returning JSON. So I'll say $json = $this->serialize($programmer). And we can't use JsonResponse anymore, or it'll encode things twice. Create a regular Response instead. Copy this and repeat the same thing in showAction(). Use a normal Response here too:

151 lines src/AppBundle/Controller/Api/ProgrammerController.php
... lines 1 - 21
public function newAction(Request $request)
... lines 24 - 33
$json = $this->serialize($programmer);
$response = new Response($json, 201);
... lines 36 - 39
$response->headers->set('Location', $programmerUrl);
return $response;
... lines 44 - 48
public function showAction($nickname)
... lines 51 - 61
$json = $this->serialize($programmer);
$response = new Response($json, 200);
return $response;
... lines 68 - 151

For listAction, life gets easier. Just put the $programmers array inside the $data array and then pass this big structure into the serialize() function:

151 lines src/AppBundle/Controller/Api/ProgrammerController.php
... lines 1 - 72
public function listAction()
$programmers = $this->getDoctrine()
$json = $this->serialize(['programmers' => $programmers]);
$response = new Response($json, 200);
return $response;
... lines 84 - 151

The serializer has no problem serializing arrays of things. Make the same changes in updateAction():

151 lines src/AppBundle/Controller/Api/ProgrammerController.php
... lines 1 - 88
public function updateAction($nickname, Request $request)
... lines 91 - 108
$json = $this->serialize($programmer);
$response = new Response($json, 200);
return $response;
... lines 114 - 151

Great! Let's check on Composer. It's done, so let's try our entire test suite:

phpunit -c app

Ok, things are not going well. One of them says:

Error reading property "avatarNumber" from available keys
(id, nickname, avatar_number, power_level)

The responses on top show the same thing: all our properties are being underscored. The JMS Serializer library does this by default... which I kinda hate. So we're going to turn it off.

The library has something called a "naming strategy" - basically how it transforms property names into JSON or XML keys. You can see some of this inside the bundle's configuration. They have a built-in class for doing nothing: it's called the "identical" naming strategy. Unfortunately, the bundle has a bug that makes this not configurable in the normal way. Instead, we need to go kung-foo on it.

Open up config.yml. I'll paste a big long ugly new parameter here:

79 lines app/config/config.yml
... lines 1 - 5
# a hack - should be configurable under jms_serializer, but the
# doesn't seem to be taken into account at all.
jms_serializer.camel_case_naming_strategy.class: JMS\Serializer\Naming\IdenticalPropertyNamingStrategy
... lines 10 - 79

This creates a new parameter called jms_serializer.camel_case_naming_strategy.class. I'm setting this to JMS\Serializer\Naming\IdenticalPropertyNamingStrategy. That is a total hack - I only know to do this because I went deep enough into the bundle to find this. If you want to know how this works, check out our Journey to the Center of Symfony: Dependency Injection screencast: it's good nerdy stuff. The important thing for us is that this will leave our property names alone.

So now if we run the test:

phpunit -c app

we still have failures. But in the dumped response, our property names are back!

Leave a comment!

  • 2018-02-05 weaverryan

    That's a good hack indeed :). Thanks for sharing Prakash S!

  • 2018-02-02 Prakash S

    The hack for identical property error in JMS Serializer not worked for me somehow. I found another hack somewhere:

    Add this in services.yml:

    alias: jms_serializer.identical_property_naming_strategy

  • 2017-08-15 Nina

    Yes, thank you.

  • 2017-08-15 Victor Bocharsky

    Hey Nina,

    I see in the output you execute "$ composer require jms/serializer" but that's not a bundle, that is just a standalone library. So instead, you need to execute "$ composer require jms/serializer-bundle" and only then register your bundle with "new JMS\SerializerBundle\JMSSerializerBundle()," in AppKernel.php. Btw, notice that there's no leading slash before JMS namespace - I see you have it. It shouldn't cause problems, but just in case. So try to stick this installation:

    Let us know if it doesn't help you.


  • 2017-08-15 Nina

    I try "to right click on vendor/ and select Synchronize 'vendor'" but it isn't help
    editor highlights
    public function registerBundles()
    $bundles = [

    new \JMS\SerializerBundle\JMSSerializerBundle(),
    in the controller when I try to use
    $user = $this->container->get('jms_serializer')
    ->serialize($user, 'json');

    'jms_serializer' also highlights

    and in the browser I have:
    Whoops, looks like something went wrong.
    (1/1) ClassNotFoundException
    Attempted to load class "JMSSerializerBundle" from namespace "JMS\SerializerBundle".
    Did you forget a "use" statement for another namespace?
    in AppKernel.php (line 19)
    at AppKernel->registerBundles()
    in Kernel.php (line 450)
    at Kernel->initializeBundles()
    in Kernel.php (line 116)
    at Kernel->boot()
    in Kernel.php (line 168)
    at Kernel->handle(object(Request))
    in app_dev.php (line 29)
    at require('C:\\OpenServer\\domains\\\\web\\app_dev.php')
    in router.php (line 42)

  • 2017-08-15 Nina

    I think installation was successful

    > composer require jms/serializer
    Using version ^1.8 for jms/serializer
    ./composer.json has been updated
    Loading composer repositories with package information
    Updating dependencies (including require-dev)
    Package operations: 5 installs, 0 updates, 0 removals
    - Installing phpoption/phpoption (1.5.0): Loading from cache
    - Installing phpcollection/phpcollection (0.5.0): Loading from cache
    - Installing jms/parser-lib (1.0.0): Loading from cache
    - Installing jms/metadata (1.6.0): Loading from cache
    - Installing jms/serializer (1.8.1): Loading from cache
    Writing lock file
    Generating autoload files
    > Incenteev\ParameterHandler\ScriptHandler::buildParameters
    Updating the "app/config/parameters.yml" file
    > Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::buildBootstrap
    > Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::clearCache

    // Clearing the cache for the dev environment with debug true

    [OK] Cache for the "dev" environment (debug=true) was successfully cleared.

    > Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::installAssets

    Trying to install assets as relative symbolic links.

    -- -------- ----------------
    Bundle Method / Error
    -- -------- ----------------

    [OK] All assets were successfully installed.

    > Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::installRequirementsFile
    > Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::prepareDeploymentTarget

  • 2017-08-15 weaverryan

    Hey Nina!

    Hmm. Do you actually get an error when you run the code? Or is it simply that your editor isn't happy? If you get an error, it seems to me that somehow, the installation of the bundle was *not* successful. If the error is only in your editor, try clearing your browser's cache (I assume PhpStorm? One trick is to right click on vendor/ and select Synchronize 'vendor' to make sure it sees the new files).


  • 2017-08-15 Nina

    I install jms with
    composer require jms/serializer-bundle

    but when I add
    new \JMS\SerializerBundle\JMSSerializerBundle(),
    into AppKernel.php
    in the

    public function registerBundles()
    $bundles = [

    new \JMS\SerializerBundle\JMSSerializerBundle(),

    it's highlights and say "undefined namespace for SerializerBundle"
    "undefined class JMSSerializerBundle"
    How can I to fix this?

  • 2017-05-17 weaverryan

    Hey Mike!

    First, LOVE your comments below - made our morning!

    The Symfony serializer *is* robust enough now. Though, the JMSSerializer is a bit more configurable. For example, if you have a class and only want to expose *some* of the properties and/or maybe have a few "extra" properties (where the serializer calls a get* method to get the value for a field that is not a real value), you can do that in both (you'll use the Group annotation with the Symfony serializer to only serialize some fields). But that's about as far as the customizations go with the Symfony serializer... and I think that's by design (and kind of a good thing). If you have a resource (to use our fancy REST terms) and it begins to look *quite* different than your entity class, it might be better to create a new, "model" (non-entity) class and serialize this (e.g. create your new object and manually take the data from your entity and put it onto your model class - it's a bit of manual labor, but clear). The Symfony serializer pushes this approach a bit more. It's more work... but you also end up with clean model classes that exactly match your API (versus an entity class with a ton of annotations to modify it).

    Honestly, I'm still on the fence a bit between the two. I'm tending to use the Symfony serializer more... but recently, the JMS serializer appears to have become "unabandoned" and is active again. Oh choices!


  • 2017-05-16 Mike Ritter

    Hey! Is symfony serializer robust enough now?

    Only started using serializers (jms ans symfony) recently.

  • 2017-05-16 Mike Ritter

    that little angry face on the icon!

    you're killin' me Smalls!

  • 2017-05-16 Mike Ritter

    "It'll turn into a whole soap opera". Great lines like this every video, but this is my favorite!

    You guys are so stinkin' funny!

    And cute.

    And smart.

    Love your tuts!

  • 2017-01-04 weaverryan

    Awesome all around :D. Good luck and thanks for the nice words!

  • 2017-01-03 Greg

    Hey Ryan

    Thank you for your answer, it is exactly what I am thinking. When I tried to use Symfony serializer in this tuto I got back all my entity with the entire user's relation (so included the password). JMSSerializer has a great powerful annotation to handle this problem.
    I saw in the documentation the @Groups annotation for SF Serializer but I don't know yet how it is working ;)
    thanks for the tip about the SF Serializer's configuration.

    Again have a great year for all the KnpUniversity's team and make us like usually worderful tutos

  • 2017-01-02 weaverryan

    Hey Greg!

    Wow - happy new year to you too - and all the same wonderful wishes :).

    This is a GREAT question... and one that I struggle with myself. The JMSSerializer is still more powerful in my opinion than the Symfony serializer, with many more annotations to help you customize things. However, it's also basically abandoned, and much more complex. The Symfony serializer is still under active development and is a very high quality library!

    So, right now, the best option comes down to a case-by-case basis, based on the *developer*. With JMSSerializer, since it has so many annotations, you can usually serialize your entities directly. If you need to tweak the name of a property (e.g. firstName should be "user_first_name" on JSON), that's easy to do. For more junior developers, I think its flexibility through all the annotations gives it a lower barrier to entry (the Symfony serializer *does* however have the @Groups annotation, which handles most cases - but isn't as user-friendly as the @Expose in JMS). For more senior developers, I recommend using the Symfony serializer. The trick is that if you have a JSON representation that looks sufficiently different than your entity, there are no annotations you can use that will help you "tweak" things so that you can serialize your entity directly. Instead, when I use the Symfony serializer, I usually create specific "model" classes and serialize them. For example, instead of serializing a Product entity, I'll create an API\Product class with the exact properties that I want in my JSON. I will then manually create the API\Product class from my Product entity's data and then serialize it. This is more work (and can seem like a lot to some developers). But, it also means that you have a really clean API: you can just open a directory full of simple PHP classes that each perfectly model a resource.

    Phew! I hope that helps! About your code question, it's kind of right... but you're doing too much work! In the Symfony framework, Symfony comes with a pre-configured serializer service (you just need to active it in config.yml). Then you can just $this->get('serializer')->serialize($data, 'json').


  • 2017-01-01 Greg

    Hey KnpUniversity

    In the first time, Happy new year and all my best wishes for this new year (love, peace and more symfony code :p )
    I have just a little question about the serialization, now with sf 3 is it better to use the Symfony serializer ?
    And if it is yes is it correct to replace this method like that ?

    private function serialize($data)
    $encoders = array(new XmlEncoder(), new JsonEncoder());
    $normalizers = array(new ObjectNormalizer());

    $serializer = new Serializer($normalizers, $encoders);
    return $serializer->serialize($data, 'json');

    Thanks again for your good work.