Buy

Serializer Annotations

Serializer Annotations

Alright, now let me show you one other powerful thing about the serializer. The control you have over your objects! By default, it serializes all of your properties. But let’s say we don’t want the userId to be serialized. After all this isn’t really an important field for the API, it’s just the id of the user that created the programmer.

To start, open up the feature file and add a line to test that this property isn’t in the response:

# features/api/programmer.feature
# ...

Scenario: GET one programmer
  Given the following programmers exist:
    | nickname   | avatarNumber |
    | UnitTester | 3            |
  When I request "GET /api/programmers/UnitTester"
  # ...
  And the "userId" property should not exist

We’re using a custom Behat context that I created for this project with a lot of these nice sentences. To see a list of all of them, you can run behat with the -dl option, which stands for “definition list”:

php vendor/bin/behat -dl

Try running the tests again. It should fail, and it does - saying that the userId property should not exist, but we can see that it in fact is there.

Configuring the Serializer with Annotations

As soon as you want to take control over what properties are returned, we’re going to use annotations. Let’s look at their documentation first and find the Annotation Reference section - this is by far the most interesting page. The first item on the list is what we need, but there’s a huge list of annotations that give you all sorts of control.

Remember, whenever you use an annotation, you need a use statement for it. PHPStorm is tagging in to help me auto-complete the ExclusionPolicy class. Then I’ll remove the last part and alias this to Serializer. This will allow us to use any of the JMS serializer annotations by starting with @Serializer:

// src/KnpU/CodeBattle/Model/Programmer.php
// ...

/**
 * @Serializer\ExclusionPolicy("all")
 */
class Programmer
{
    // ...
}

For example, on top of the class, we can say @Serializer\ExclusionPolicy("all"). We’ve now told the serializer to not serialize any of the properties in this class, unless we tell it to specifically. Whereas before, it was serializing everything.

To actually include things, we whitelist them with the @Serializer\Expose annotation. I’ll copy this and use it on nickname, avatarNumber, tagLine and powerLevel fields:

// src/KnpU/CodeBattle/Model/Programmer.php
// ...

/**
 * @Serializer\ExclusionPolicy("all")
 */
class Programmer
{
    // ...

    public $id;

    /**
     * @Serializer\Expose
     */
    public $nickname;

    /**
     * @Serializer\Expose
     */
    public $avatarNumber;

    /**
     * @Serializer\Expose
     */
    public $tagLine;

    public $userId;

    /**
     * @Serializer\Expose
     */
    public $powerLevel = 0;
}

This is just one of the customizations you can make with annotations.

Now let’s re-run the test:

php vendor/bin/behat

Success! This time the userId is not returned in our JSON.

If you want to know more, check out that annotation reference section. But we’re also going to do more in the next videos.

Leave a comment!

  • 2015-02-20 Joan

    Thanks Ryan. I am not using your createObjectFromData() and I can't recall why, but the fix works for my own crappy version. Thanks :-)

  • 2015-02-19 weaverryan

    Hey Joan!

    Ok, here's what's going on. If you don't specify a type, then the JSON will represent what the value is. So if the value is a string "5", you'll get "5" in the JSON. If it's a PHP integer 5, you'll get 5 without quotes. The reason you're *always* seeing quotes is just due to a shortcoming in the "ORM" that I built for this tutorial - it *always* fetches values from the DB as strings and sets them. In other words, if you dump a Programmer object after querying, you should see that the "integer" values are actually PHP strings. You should see a difference if you add something like the following to the bottom of the BaseRepository::createObjectFromData() function:


    if (is_numeric($val)) {
    $val = (float) $val;
    }
    // this line is already there - so put the if statement right above
    $object->$columnName = $val;

    This causes true PHP numbers to be set, which removes the quotes from the JSON. Now the Type annotation also relates to this, but not in a way that looks super concrete to me. For example, if I set a true number, but mark that field as @Serializer\Type("string"), I see quotes in the JSON. But if I do the opposite - set a string on the object but mark the field as an "integer" - then I *still* see a string. It's a bit odd.

    That's a long way of saying that the serializer should be creating JSON that represents what you have in PHP. and in this case, my ORM is always giving string values.

    I hope that helps!

  • 2015-02-18 Joan

    Hey Ryan, I am trying that my responses do not return integers as strings, i.e "id": 3, instead of "id": "3". I have used the @Type("integer") option of the JMSSerializer but apparently it has no effect in the output. Do you have any idea of how can this be achieved?