fracz
fracz

Reputation: 21268

How to serialize empty array to JSON object (curly braces) with JMS Serializer in Symfony

Having the following PHP class

class SampleObject 
{
    private $map;

    private $array;

    public function getMap(): array 
    {
        return $map;
    }

    public function setMap(array $map) 
    {
        $this->map = $map;
    }

    public function getArray(): array
    {
        return $this->array;
    }

    public function setArray(array $array) {
        $this->array = $array;
    }
}

and two instances:

$inst1 = new SampleObject();
$inst2 = new SampleObject();
$inst1->setMap(['key' => 'value']);
$inst1->setArray([1]);
$inst2->setMap([]);
$inst2->setArray([]);

When they are serialized with JMS Serializer to json, the first one becomes:

{"map": {"key": "value"}, "array": [1]}

and the second one:

{"map": [], "array": []}

How to force the serializer to serialize the second object as {"map": {}, "array": []}?

Upvotes: 1

Views: 2966

Answers (2)

smatyas
smatyas

Reputation: 753

You can modify the array serialization behavior by setting context information on the serialize method (or using annotations etc.)

You'll find these examples about serializing arrays and hashes in the cookbook:

<?php

// default (let the PHP's json_encode function decide)
$serializer->serialize([1, 2]); //  [1, 2]
$serializer->serialize(['a', 'b']); //  ['a', 'b']
$serializer->serialize(['c' => 'd']); //  {"c" => "d"}

// same as default (let the PHP's json_encode function decide)
$serializer->serialize([1, 2], SerializationContext::create()->setInitialType('array')); //  [1, 2]
$serializer->serialize([1 => 2], SerializationContext::create()->setInitialType('array')); //  {"1": 2}
$serializer->serialize(['a', 'b'], SerializationContext::create()->setInitialType('array')); //  ['a', 'b']
$serializer->serialize(['c' => 'd'], SerializationContext::create()->setInitialType('array')); //  {"c" => "d"}

// typehint as strict array, keys will be always discarded
$serializer->serialize([], SerializationContext::create()->setInitialType('array<integer>')); //  []
$serializer->serialize([1, 2], SerializationContext::create()->setInitialType('array<integer>')); //  [1, 2]
$serializer->serialize(['a', 'b'], SerializationContext::create()->setInitialType('array<integer>')); //  ['a', 'b']
$serializer->serialize(['c' => 'd'], SerializationContext::create()->setInitialType('array<string>')); //  ["d"]

// typehint as hash, keys will be always considered
$serializer->serialize([], SerializationContext::create()->setInitialType('array<integer,integer>')); //  {}
$serializer->serialize([1, 2], SerializationContext::create()->setInitialType('array<integer,integer>')); //  {"0" : 1, "1" : 2}
$serializer->serialize(['a', 'b'], SerializationContext::create()->setInitialType('array<integer,integer>')); //  {"0" : "a", "1" : "b"}
$serializer->serialize(['c' => 'd'], SerializationContext::create()->setInitialType('array<string,string>')); //  {"d" : "d"}

Upvotes: 1

fracz
fracz

Reputation: 21268

As @EmanuelOster suggested in the comment, the custom handler can be used for this purpose. While the solution is not perfect (an annotation on the field would be much better), it works. Here is a sample handler

class SampleObjectSerializer implements SubscribingHandlerInterface {
    public static function getSubscribingMethods() {
        return [
            [
                'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
                'format' => 'json',
                'type' => SampleObject::class,
                'method' => 'serialize',
            ],
        ];
    }

    public function serialize(JsonSerializationVisitor $visitor, SampleObject $object, array $type, Context $context) {
        return [
            'array' => $object->getArray(),
            'map' => $this->emptyArrayAsObject($object->getMap()),
        ];
    }

    /**
     * Forces to searialize empty array as json object (i.e. {} instead of []).
     * @see https://stackoverflow.com/q/41588574/878514
     */
    private function emptyArrayAsObject(array $array) {
        if (count($array) == 0) {
            return new \stdClass();
        }
        return $array;
    }
}

If using Symfony, you need to register it.

Upvotes: 1

Related Questions