9

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.

7
  • I'm afraid that there isn't an easy way to do it (without having to write complex introspection code), adding elseif (null === $val) { return false; } here github.com/symfony/symfony/blob/2.8/src/Symfony/Component/… resolves your problem, but to do that you need rewrite a lot of code because almost all methods are private into XmlEncoder :( Commented Nov 24, 2016 at 15:53
  • Never used it but maybe using JMS serializer could help with that jmsyst.com/bundles/JMSSerializerBundle hope it helps Commented Nov 24, 2016 at 16:07
  • @Yonel thanks indeed I would like to avoid to write in symfony code. I am not alone ! Isn't it possible to override the XmlEncoder in a non intruisive way for that ? Commented Nov 24, 2016 at 16:13
  • @Thomas as you see at the end of my question I am aware of JMS serializer. I want to stick with Symfony one. Commented Nov 24, 2016 at 16:13
  • 1
    Well I found you a simple solution \o/ :) Commented Nov 24, 2016 at 17:27

3 Answers 3

12

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>
Sign up to request clarification or add additional context in comments.

3 Comments

This works like a charm and this is a very simple solution. Too bad I cannot vote twice ! Thank you :)
@coincoin +1 :)
It’s even easier to implement and test by composing an instance of Symfony\Component\Serializer\Normalizer\ObjectNormalizer into a simple implementation of Symfony\Component\Serializer\Normalizer\NormalizerInterface with the suggested filtering above - less baggage, fully tested.
4

You can use AbstractObjectNormalizer::SKIP_NULL_VALUES flag to skip null values when normalizing:

$json = $serializer->serialize(
    $user,
    JsonEncoder::FORMAT,
    [AbstractObjectNormalizer::SKIP_NULL_VALUES => true]
);

Source: https://lindevs.com/skip-null-values-during-serialization-in-symfony/

Comments

3

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.