3

Let's say we have a simple object to serialize with a nested object:

class User implements \JsonSerializable
{

    private $name;
    private $email;
    private $address;

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

The nested object:

class Address implements \JsonSerializable
{

    private $city;
    private $state;

    public function jsonSerialize()
    {
        return [
            'city'  => $this->city,
            'state' => $this->state
        ];
    }
}

We use json_encode() to serialize, this will use natively JsonSerializable::jsonSerialize():

$json = json_encode($user);

If $name and $state are null, how to get this:

{
    "email": "john.doe@test.com",
    {
        "city": "Paris"
    }
}

instead of this:

{
    "name": null,
    "email": "john.doe@test.com",
    {
        "city": "Paris",
        "state": null
    }
}
Kwadz
  • 2,206
  • 2
  • 24
  • 45
  • There is no way to do it automatically, you have to go through the data being serialized and unset() all the null values. If it's recursive you can use something array_walk – Johnny Dec 13 '16 at 15:38
  • Can you give an example which manage recursivity? – Kwadz Dec 13 '16 at 15:50

2 Answers2

3

wrap array_filter around the returned arrays, e.g.

public function jsonSerialize()
    return array_filter([
        'city'  => $this->city,
        'state' => $this->state
    ]);
}

This will strip any entries equal to false (loosely compared), which includes any null values, but also 0s and false. If you need strict, e.g. only nulls, supply the following callback:

function($val) { return !is_null($val); }

See the documentation for array_filter:

(PHP 4 >= 4.0.6, PHP 5, PHP 7)

Iterates over each value in the array passing them to the callback function. If the callback function returns true, the current value from array is returned into the result array. Array keys are preserved.

Another option would be to use JMX Serializer which is a highly configurable serializer for JSON, XML and YAML. It's much heavier though. See Exclude null properties in JMS Serializer for details.

Community
  • 1
  • 1
Gordon
  • 312,688
  • 75
  • 539
  • 559
0

My comment was not correct, array_walk_recursive() can alter values but cannot unset them, so I made a custom recursive function, just a few lines more of code:

<?php

$a = (object) array(
  'first' => null,
  'second' => 10,
  'third' => (object) array(
    'second-first' => 100,
    'second-second' => null,
    'second-third' => 300
  ),
  'fourth' => 20,
  'fifth' => null,
  'sixth' => 10);

function remove_null_values($a) {
  $ret = (is_array($a) ? array() : new stdClass());
  foreach ($a as $k => $v) {
    if (is_array($v) || is_object($v)) {
      if (is_object($ret))
        $ret->$k = remove_null_values($v);
      else
        $ret[$k] = remove_null_values($v);
    }
    elseif (!is_null($v)) {
      if (is_object($ret))
        $ret->$k = $v;
      else
        $ret[$k] = $v;
    }
  }
  return $ret;
}

Output:

object(stdClass)#2 (6) {
  ["first"]=>
  NULL
  ["second"]=>
  int(10)
  ["third"]=>
  object(stdClass)#1 (3) {
    ["second-first"]=>
    int(100)
    ["second-second"]=>
    NULL
    ["second-third"]=>
    int(300)
  }
  ["fourth"]=>
  int(20)
  ["fifth"]=>
  NULL
  ["sixth"]=>
  int(10)
}
object(stdClass)#3 (4) {
  ["second"]=>
  int(10)
  ["third"]=>
  object(stdClass)#4 (2) {
    ["second-first"]=>
    int(100)
    ["second-third"]=>
    int(300)
  }
  ["fourth"]=>
  int(20)
  ["sixth"]=>
  int(10)
}

EDIT: I updated the code to work both nested arrays and objects, in case you don't need the arrays part functionality it's trivial to factor it out, I'll let you handle the details

Johnny
  • 1,770
  • 12
  • 16