Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serialize using jsonSerialize() if object implements JsonSerializable #846

Closed
ramsey opened this issue Nov 29, 2017 · 10 comments
Closed

Serialize using jsonSerialize() if object implements JsonSerializable #846

ramsey opened this issue Nov 29, 2017 · 10 comments

Comments

@ramsey
Copy link

ramsey commented Nov 29, 2017

Q A
Bug report? no
Feature request? yes
BC Break report? no
RFC? no

Serialize using jsonSerialize() if object implements JsonSerializable

I received an issue on ramsey/uuid-doctrine, where a user describes a situation where the Ramsey\Uuid\Uuid object is converted to a JSON object of its properties, rather than the expected string representation. See ramsey/uuid-doctrine#50.

In using ramsey/uuid-doctrine, they expected the serialized JSON for the database field to include the string UUID and nothing else. Since Uuid implements the JsonSerializable interface, I would also have this same expectation. However, jms/serializer doesn't appear to respect JsonSerializable, so it doesn't serialize the object according to its own rules. Instead, it seems to iterate over the properties of the object and creates a JSON object based on those.

I'm requesting that jms/serializable implement a feature that detects whether an object implements JsonSerializable, and if so, call json_encode() on the object instead.

Example

Here's an example showing how jms/serializer renders a Uuid object.

composer.json:

{
    "require": {
        "ramsey/uuid": "^3.7",
        "jms/serializer": "^1.9"
    }
}

serialize-uuid.php:

<?php
require_once 'vendor/autoload.php';

$uuid = \Ramsey\Uuid\Uuid::uuid4();
$serializer = JMS\Serializer\SerializerBuilder::create()->build();

echo "Using jms/serializer:\n\n";
echo $serializer->serialize($uuid, 'json');
echo "\n\n";

echo "Using json_encode():\n\n";
echo json_encode($uuid);
echo "\n\n";

Running this script, we see the following output:

$ php serialize-uuid.php
Using jms/serializer:

{"factory":{"codec":{"builder":{"converter":{}}},"node_provider":{"node_providers":[{},{}]},"number_converter":{},"random_generator":{},"time_generator":{"node_provider":{"node_providers":[{},{}]},"time_converter":{},"time_provider":{}},"uuid_builder":{"converter":{}}},"codec":{"builder":{"converter":{}}},"fields":{"time_low":"4abe89df","time_mid":"76e6","time_hi_and_version":"452a","clock_seq_hi_and_reserved":"97","clock_seq_low":"69","node":"c93997076b53"},"converter":{}}

Using json_encode():

"4abe89df-76e6-452a-9769-c93997076b53"
@goetas
Copy link
Collaborator

goetas commented Nov 30, 2017

See here the a simple solution for the issue.

Regarding the feature request, the jms/serializer "json" serialization is not based on "json_encode" conventions so to me it seems logical to not use JsonSerializable.

The JMS serializer tries to guess the type of the object and visit its properties. When the @Type is specified the serializer does not need to guess the type and can use the serialization rules specified for it.

A Is also possible to specify @Type("JsonSerializable") and provide a custom handler for it.

(obviously all this works when talking about "serialization", "de-serialization" is a different problem)

@din-gi
Copy link

din-gi commented Nov 30, 2017

Hey,

Thanks for the response.

Here's some of my code:

use \JMS\Serializer\Annotation\Type;

class StoreMonitoredProducts
{
    /**
	 * @var \Ramsey\Uuid\Uuid
	 *
	 * @ORM\Id
	 * @Type("string")
	 * @ORM\Column(type="uuid", unique=true)
	 * @ORM\GeneratedValue(strategy="CUSTOM")
	 * @ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
     */
    private $storeMonitoredProductId;
}

This still gives me the following response:

{
  "data": [
    {
      "storeMonitoredProductId": {
        "factory": {
          "codec": {
            "builder": {
              "converter": {}
            }
          },
          "nodeProvider": {
            "nodeProviders": [
              {},
              {}
            ]
          },
          "numberConverter": {},
          "randomGenerator": {},
          "timeGenerator": {
            "nodeProvider": {
              "nodeProviders": [
                {},
                {}
              ]
            },
            "timeConverter": {},
            "timeProvider": {}
          },
          "uuidBuilder": {
            "converter": {}
          }
        },
        "codec": {
          "builder": {
            "converter": {}
          }
        },
        "fields": {
          "time_low": "00532d60",
          "time_mid": "6a9c",
          "time_hi_and_version": "58b4",
          "clock_seq_hi_and_reserved": "c9",
          "clock_seq_low": "e7",
          "node": "c566472cfc4c"
        },
        "converter": {}
      }
    }
  ]
}

Also,
I would love good example on this serialization and de-serialization matters.
Thanks!

@goetas
Copy link
Collaborator

goetas commented Nov 30, 2017

did you clear your cache?

@din-gi
Copy link

din-gi commented Nov 30, 2017

Hey, I didn't.. but after your request I did remove Symfony cache which is under var/cache (which includes the jms_serializer cache) and it is still the same response.

@goetas
Copy link
Collaborator

goetas commented Nov 30, 2017

Weird, I'm using Ramsey\Uuid in half of my projects and @Type("string") worked always

@goetas
Copy link
Collaborator

goetas commented Nov 30, 2017

Are you sure that metadata are loaded correctly? if you put @Type("somnethingThatDoesNotExists") what do you get?

@din-gi
Copy link

din-gi commented Nov 30, 2017

I got the same thing.. which means I might be missing something.. I will get back to this in the evening and try to look a bit more as I don't want to annoy you, because I probably lack some knowledge. Thanks.

I would be glad though if you can reference me to some links.

Thanks

@goetas
Copy link
Collaborator

goetas commented Nov 30, 2017

it looks that your metadata are not loaded. which version of symfony are you using?

@din-gi
Copy link

din-gi commented Nov 30, 2017

3.3.13

@goetas
Copy link
Collaborator

goetas commented Nov 30, 2017

you do not need to build "manually" the serializer instance, just use

public function generateGoodResponse($jsonResponse)
{
    $jsonContent = $this->serializer->serialize($jsonResponse, 'json');
    $response = new Response($jsonContent);
    $response->headers->set('Content-Type', 'application/json');

    return $response;
}

$this->serializer is the service that should be injected into your controller. Its name is jms_serializer.

if you use using autowiring just add a typehint \JMS\Serializer\SerializerInterface otherwise as said the service name is jms_serializer.

Closing as it is not a bug but a config issue

@goetas goetas closed this as completed Nov 30, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants