symfony/serializer
Symfony Serializer component for converting object graphs and data structures to/from arrays and formats like JSON or XML. Supports powerful normalizers/encoders, metadata, naming and type handling—ideal for APIs, messaging, and data interchange.
Installation:
composer require symfony/serializer
For Laravel, use symfony/serializer-pack for a pre-configured bundle (if needed).
Basic Usage:
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
$encoders = [new JsonEncoder()];
$normalizers = [new ObjectNormalizer()];
$serializer = new Serializer($normalizers, $encoders);
// Serialize
$data = $serializer->serialize($object, 'json');
// Deserialize
$object = $serializer->deserialize($json, 'object', 'App\\Model\\ClassName');
First Use Case: Convert a Laravel Eloquent model to JSON for an API response:
$user = User::find(1);
$json = $serializer->serialize($user, 'json');
return response($json, 200, ['Content-Type' => 'application/json']);
Serializer: Main class for serialization/deserialization.Normalizer: Converts objects to/from arrays (e.g., ObjectNormalizer).Encoder: Handles format conversion (e.g., JsonEncoder, XmlEncoder).NameConverter: Maps property names (e.g., MetadataAwareNameConverter for Doctrine).Use ObjectNormalizer with JsonEncoder for Eloquent models:
$serializer = new Serializer([new ObjectNormalizer()], [new JsonEncoder()]);
return response($serializer->serialize($model, 'json'));
Laravel Integration:
// In a controller
public function show(Model $model)
{
return response()->json($this->serializer->serialize($model, 'json'));
}
Deserialize JSON into Eloquent models:
$data = json_decode(request()->getContent(), true);
$model = $serializer->deserialize($data, Model::class, 'json');
Validation-First Approach:
$validator = Validator::make($data, Model::$rules);
if ($validator->fails()) {
return response()->json($validator->errors(), 422);
}
$model = $serializer->deserialize($data, Model::class, 'json');
Extend ObjectNormalizer for domain-specific logic:
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class CustomNormalizer implements NormalizerInterface
{
public function normalize($object, string $format = null, array $context = [])
{
return [
'id' => $object->id,
'custom_field' => $object->getCustomField(),
];
}
public function supportsNormalization($data, string $format = null): bool
{
return $data instanceof YourModel;
}
}
Register it in the Serializer constructor:
$normalizers = [new ObjectNormalizer(), new CustomNormalizer()];
Use @Groups annotations to control which fields are serialized:
use Symfony\Component\Serializer\Annotation\Groups;
class User
{
#[Groups(['public'])]
public $name;
#[Groups(['admin'])]
public $email;
}
Serialize with groups:
$serializer->serialize($user, 'json', [
AbstractNormalizer::GROUPS => ['public'],
]);
Handle circular references in Eloquent relationships:
$normalizer = new ObjectNormalizer();
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getId();
});
Bind the serializer in AppServiceProvider:
public function register()
{
$this->app->singleton(Serializer::class, function ($app) {
$encoders = [new JsonEncoder()];
$normalizers = [new ObjectNormalizer()];
return new Serializer($normalizers, $encoders);
});
}
Inject via constructor:
public function __construct(private Serializer $serializer) {}
Automatic JSON Conversion:
Use ObjectNormalizer with JsonEncoder for automatic model-to-JSON conversion in API responses.
$response = response()->json($this->serializer->serialize($model, 'json'));
Mass Assignment: Deserialize JSON into Eloquent models safely:
$data = $serializer->deserialize($request->json()->all(), Model::class, 'json');
$data->save(); // Handles mass assignment via fillable fields
@ApiResource entities:
#[ApiResource(
normalizationContext: ['groups' => ['public']],
denormalizationContext: ['groups' => ['public']]
)]
class User {}
Mock Serializer:
$serializer = $this->createMock(Serializer::class);
$serializer->method('serialize')->willReturn('{"id":1}');
Assert JSON:
$json = $serializer->serialize($model, 'json');
$this->assertJson($json);
Cache Normalizers:
Reuse Serializer instances (e.g., as a singleton in Laravel).
$serializer = app(Serializer::class); // Reuse across requests
Avoid Redundant Normalization:
Use AbstractNormalizer::SKIP_MISSING_NULL_VALUES to skip null checks:
$serializer->serialize($model, 'json', [
AbstractNormalizer::SKIP_MISSING_NULL_VALUES => true,
]);
User->posts->author->user).setCircularReferenceHandler:
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getId();
});
->with() to eager-load and avoid N+1 queries."string" for a DateTime field).@Type annotations or custom normalizers:
#[Type(name: "datetime")]
public ?DateTimeInterface $createdAt = null;
AbstractNormalizer::IGNORED_ATTRIBUTES to log skipped fields:
$serializer->serialize($model, 'json', [
AbstractNormalizer::IGNORED_ATTRIBUTES => true,
]);
ObjectNormalizer::IGNORED_ATTRIBUTES or use @AccessType:
#[AccessType("public_method")]
class Model {}
getAttributes() in models to expose private properties:
public function getAttributes()
{
return ['id', 'name', 'created_at'];
}
@Groups annotations may not work as expected with nested objects.$serializer->serialize($model, 'json', [
AbstractNormalizer::GROUPS => ['group1', 'group2'],
]);
allow_invalid_values in BackedEnumNormalizer:
$normalizer = new BackedEnumNormalizer();
$normalizer->setAllowInvalidValues(true);
@SerializedName orHow can I help you explore Laravel packages today?