Search code examples
jsonsymfonykeyjmsserializerbundlejms-serializer

JMSSerializerBundle not mapping key equals to 0


i'm using JMSSerializerBundle and i'm noticing a strange behavior with serialization.

I'am using $jsonItems = $serializer->serialize($invoiceItems, 'json'); where $invoiceitems is the following :

array(2) {
  ["services"]=>
  array(3) {
    [1]=>
    array(1) {
      [0]=>
      object(stdClass)#757 (26) {
        ["__CLASS__"]=>
        string(43) "Evo\BackendBundle\Entity\InvoiceItemService"
        ["id"]=>
        int(4)
      }
    }
    [2]=>
    array(0) {
    }
    [3]=>
    array(1) {
      [1]=>
      object(stdClass)#846 (26) {
        ["__CLASS__"]=>
        string(43) "Evo\BackendBundle\Entity\InvoiceItemService"
        ["id"]=>
        int(7)
      }
    }
  }
  ["marchandises"]=>
  array(0) {
  }
}

And I get the following JSON :

{"services"{
    "1":[
        {"realDiscount":0,"id":4,"createdAt":"2014-08-22","description":"test 2","type":1,"totalFlat":75,"discountFlat":0,"discountFlatType":"%","chargedHours":0,"chargedMinutes":0,"hourlyRate":0,"discountHourly":0,"discountHourlyType":"%","chargedQuantity":0,"unitPrice":0,"discountQuantity":0,"discountQuantityType":"%","total":75,"discount":0,"discountType":"%","position":1,"chargedTime":0}
    ],
    "2":[],
    "3":{
        "1":{"realDiscount":0,"id":7,"createdAt":"2014-08-22","description":"test 22","type":3,"totalFlat":0,"discountFlat":0,"discountFlatType":"%","chargedHours":0,"chargedMinutes":0,"hourlyRate":0,"discountHourly":0,"discountHourlyType":"%","chargedQuantity":50,"unitPrice":1,"discountQuantity":0,"discountQuantityType":"%","total":50,"discount":0,"discountType":"%","position":1,"chargedTime":0}
    }
},
"marchandises":[]}

Why in my JSON string, services[1] is formatted differently than services[3] ? If I refer to my PHP array, the only difference is that the first key of services[1] is 0, while the first key of services[3] is 1. It's like JMS Serializer is not mapping the key when it's equal to 0. In the end, i'd like to keep key equals to 0 in the JSON string.

How can I do that ?


Solution

  • To apply this logic to your whole project:

    Just add those lines to your serializer config (config.yml):

    jms_serializer:    
        visitors:
            json:
                options: [JSON_FORCE_OBJECT]
    

    Source: http://jmsyst.com/bundles/JMSSerializerBundle/master/configuration#extension-reference


    To apply this logic per call:

    You'll have to create an EventSubscriber

    <?php
    
    namespace Acme\Api\Serializer;
    
    use JMS\Serializer\EventDispatcher\Events;
    use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
    use JMS\Serializer\EventDispatcher\PreSerializeEvent;
    use JMS\Serializer\EventDispatcher\ObjectEvent;
    use JMS\Serializer\JsonSerializationVisitor;
    
    class JsonSerializationSubscriber implements EventSubscriberInterface
    {
        /**
         * {@inheritdoc}
         */
        public static function getSubscribedEvents()
        {
            return array(
                array('event' => Events::PRE_SERIALIZE, 'method' => 'onPreSerialization', 'format' => 'json'),
                array('event' => Events::POST_SERIALIZE, 'method' => 'onPostSerialization', 'format' => 'json')
            );
        }
    
        /**
         * @param PreSerializeEvent $event
         */
        public function onPreSerialization(PreSerializeEvent $event)
        {
            $context = $event->getContext();
    
            if (!$context->attributes->contains('json_encode_options')) {
                return;
            }
    
            $visitor = $event->getVisitor();
    
            if ($visitor instanceof JsonSerializationVisitor) {
                $options = $visitor->getOptions();
                $context->setAttribute('__old_json_encode_options', $options);
                $visitor->setOptions($options | $context->attributes->get('json_encode_options'));
            }
        }
    
        /**
         * @param ObjectEvent $event
         */
        public function onPostSerialization(ObjectEvent $event)
        {
            $context = $event->getContext();
    
            if (!$context->attributes->contains('__old_json_encode_options')) {
                return;
            }
    
            $visitor = $event->getVisitor();
    
            if ($visitor instanceof JsonSerializationVisitor) {
                $visitor->setOptions($context->attributes->get('__old_json_encode_options'));
            }
        }
    }
    

    Register the EventSubscriber in your config file:

    services:
        acme.serializer.subscriber.json:
            class: Acme\Api\Serializer\JsonSerializationSubscriber
    
            tags:
                - {name: jms_serializer.event_subscriber}
    

    Then whenever you want to use it:

    $context = new SerializationContext();
    $context->setAttribute('json_encode_options', JSON_FORCE_OBJECT);
    
    $this->get('serializer')->serialize($var, 'json', $context);