Home » Php » php – Do not show null element with Symfony serializer

php – Do not show null element with Symfony serializer

Posted by: admin July 12, 2020 Leave a comment

Questions:

So I have many classes I want to serialize with Symfony serializer. For instance

class Foo
{
  public $apple = 1;
  public $pear = null;

  public function serialize() {
    Utils::serialize($this);
  }
}

which I serialize with the following serialize() call :

class Utils {
    public static function serialize($object) {
      $encoder = new XmlEncoder();
      $normalizer = new ObjectNormalizer();
      $serializer = new Serializer(array($normalizer), array($encoder));
      $str = $serializer->serialize($object, 'xml')
    }
}

The output produced gives me:

<apple>1</apple><pear/>

The output expected should be:

<apple>1</apple>

I took a look at the Symfony 2.8 doc and managed to find a quick solution by using $normalizer->setIgnoredAttributes("pear").

So the improved serialize static function looks like this

class Utils {
  public static function ignoreNullAttributes($object) {
    $ignored_attributes = array();
    foreach($object as $member => $value) {
      if (is_null($object->$member)) {
        array_push($ignored_attributes, $member);
      }
     }
     return $ignored_attributes;
   }

    public static function serialize($object) {
      $encoder = new XmlEncoder();
      $normalizer = new ObjectNormalizer();
      $normalizer->setIgnoredAttributes(Utils::ignoreNullAttributes($object));
      $serializer = new Serializer(array($normalizer), array($encoder));
      $str = $serializer->serialize($object, 'xml')
    }
}

However, this solution does not satisfy me since I have more complicated cases where different Foo can be owned by a same class. e.g.

class Bar
{
  public $foo1; // (apple=null; pear=2)
  public $foo2; // (apple=2; pear=null)

  public function serialize() {
    Utils::serialize($this);
  }
}

Here I cannot use the setIgnoredAttributes method since $foo1 and $foo2 do not have the same null elements. Furthermore, I do not call the serialize method from the child class (i.e. Foo) here so the setIgnoredAttributes is empty.

Without having to write complex introspection code, how can I hide by default null element with Symfony 2.8 serializer ? I have seen for instance that it is enabled by default with JMSSerializer.

How to&Answers:

A solution would be to extend from ObjectNormalizer class, override the normalize() method and remove all null values there:

use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;

class CustomObjectNormalizer extends ObjectNormalizer
{
    public function normalize($object, $format = null, array $context = [])
    {
        $data = parent::normalize($object, $format, $context);

        return array_filter($data, function ($value) {
            return null !== $value;
        });
    }
}

$encoders = array(new XmlEncoder());
$normalizers = array(new CustomObjectNormalizer());
$serializer = new Serializer($normalizers, $encoders);

// ...

If we have an array of Person like the one of the official documentation:

// ...

$person1 = new Person();
$person1->setName('foo');
$person1->setAge(null);
$person1->setSportsman(false);

$person2 = new Person();
$person2->setName('bar');
$person2->setAge(33);
$person2->setSportsman(null);

$persons = array($person1, $person2);
$xmlContent = $serializer->serialize($persons, 'xml');

echo $xmlContent;

The result will be those not null nodes:

<?xml version="1.0"?>
<response>
    <item key="0">
        <name>foo</name>
        <sportsman>0</sportsman>
    </item>
    <item key="1">
        <name>bar</name>
        <age>33</age>
    </item>
</response>

Answer:

There is a better solution also since November 2016 with this feature : [Serializer] XmlEncoder : Add a way to remove empty tags

You just have to put the context parameter remove_empty_tags to true like this example