Geniet van

Specify how and which data should be serialized to JSON

Over the past few years JSON has become the standard of data interchange formats. Before JSON, XML was king. XML was great at modeling complex data but it is difficult to parse and is very verbose. JSON really took off when rich AJAX driven sites were introduced as it’s very lightweight. It is in a human readable format, quick to parse and its simple key/value representation cuts out all the verbosity of XML.

To encode a value to JSON, we use the PHP function json_encode().

This function was introduced in PHP version 5.2.0 and returns a string containing the JSON representation of value. This value can be of any type except "resource".

When using OOP you will most likely encode an object to JSON. This can be simply done using

class Customer
{
    public $email = null;
    public $firstName = null;
    public $lastName = null;

    public function __construct($email, $firstName, $lastName)
    {
        $this->email = $email;
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }
}

$customer = new Customer(
    'patrick@patrickbroens.nl', 
    'Patrick',
    'Broens'
);

$json = json_encode($customer);

Although this might seem as a good idea, there is a drawback. To have the object properties in JSON, they should be public. Most of the times this is not what you want to do. Secondly, what if you want to manipulate data before it gets encoded?

Here is an approach to get this right

class Customer
{
    private $email = null;
    private $firstName = null;
    private $lastName = null;

    public function __construct($email, $firstName, $lastName)
    {
        $this->email = $email;
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }

    public function toJson()
    {
        return json_encode([
            'customer' => [
                'email' => $this->email,
                'name' => $this->firstName . ' ' $this->lastName
            ]
        ]);
    }
}

$customer = new Customer(
    'patrick@patrickbroens.nl', 
    'Patrick',
    'Broens'
);

$json = $customer->toJson();

That is already looking better. But this example is only about a single object. If you are using child objects in multiple level nesting, this can get nasty.

So let's introduce the interface JsonSerializable. It was introduced in PHP 5.4.0 and is very easy to use. Objects implementing JsonSerializable can customize their JSON representation when encoded with json_encode(). The interface only knows one abstract public method called jsonSerialize.

So instead of using a method called toJson(), who does the encoding, you can use the following to get the encoding out of the method again.  

class Customer implements \JsonSerializable
{
    private $email = null;
    private $firstName = null;
    private $lastName = null;

    public function __construct($email, $firstName, $lastName)
    {
        $this->email = $email;
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }

    public function jsonSerialize()
    {
        return [
            'customer' => [
                'email' => $this->email,
                'name' => $this->firstName . ' ' $this->lastName
            ]
        ];
    }
}

$customer = new Customer(
    'patrick@patrickbroens.nl', 
    'Patrick',
    'Broens'
);

$json = json_encode($customer);

As you can see, we implement JsonSerializable by adding the interface to our class and then adding a jsonSerialize method to the body of our class to satisfy the interfaces contract.

In the jsonSerialize method we construct and return an array of the object data, just as we did with the other examples. Once again if anything changes then we can just update this one method. You’ll notice that the jsonSerialize method just returns an array.

The magic comes when you want to trigger this method, all we have to do now is json encode an instance of this class and this method will be called automatically, the array of data returned and then encoded! Now that the class implements an interface we benefit from being able to check if this class is an instanceof JsonSerializable. If you wanted you could also type hint in methods to make sure a JsonSerializable interface is passed.

If you have multiple levels of child objects, you only have to implement the interface JsonSerializable and use the method jsonSerialize where you want different output than the public members of the class.