jms/serializer
Serialize and deserialize complex PHP object graphs to JSON or XML with flexible metadata (annotations, YAML, XML). Handles circular references, exclusion strategies, versioning, dates/intervals, and integrates with Doctrine ORM—ideal for APIs and data interchange.
Installation:
composer require jms/serializer-bundle
For Laravel, manually register the bundle in config/app.php under extra.bundles:
JMS\SerializerBundle\JMSSerializerBundle::class
Basic Usage:
use JMS\Serializer\SerializerBuilder;
use JMS\Serializer\SerializerInterface;
$builder = SerializerBuilder::create();
$serializer = $builder->build();
$data = ['name' => 'John', 'age' => 30];
// Serialize to JSON
$json = $serializer->serialize($data, 'json');
echo $json; // '{"name":"John","age":30}'
// Deserialize from JSON
$decoded = $serializer->deserialize($json, 'stdClass', 'json');
First Use Case: Convert a Laravel Eloquent model to JSON with custom formatting:
use App\Models\User;
use JMS\Serializer\SerializationContext;
$user = User::find(1);
$context = SerializationContext::create()
->setGroups(['default', 'public'])
->setSerializeNull(true);
$json = $serializer->serialize($user, 'json', $context);
Model Serialization with Groups: Use annotations or YAML/XML configs to define serialization groups:
// In User.php
use JMS\Serializer\Annotation as Serializer;
class User
{
/**
* @Serializer\ExclusionPolicy("all")
* @Serializer\Groups({"default"})
*/
public $name;
/**
* @Serializer\Groups({"admin"})
*/
public $email;
}
Serialize with:
$serializer->serialize($user, 'json', SerializationContext::create()->setGroups(['default']));
Custom Handlers for Complex Types:
Handle custom objects like Carbon or Uuid:
use JMS\Serializer\Handler\SubscribingHandlerInterface;
class CarbonHandler implements SubscribingHandlerInterface
{
public static function getSubscribingMethods()
{
return [
[
'direction' => SerializerBuilder::DIRECTION_SERIALIZATION,
'format' => 'json',
'type' => 'Carbon\Carbon',
'method' => 'serializeToJson',
],
];
}
public function serializeToJson(Carbon $date, ExchangeInterface $exchange, array $context)
{
return $date->toIso8601String();
}
}
Register in SerializerBuilder:
$builder->configureHandlers(function (HandlerRegistry $registry) {
$registry->registerSubscribingHandler(new CarbonHandler());
});
API Versioning: Use metadata factories to version responses:
$metadataFactory = MetadataFactory::create();
$metadataFactory->setMetadataCache(new FileLocatorMetadataFactory([
__DIR__.'/metadata',
]));
$builder->setMetadataFactory($metadataFactory);
Create versioned configs in metadata/v1/, metadata/v2/, etc.
Deserialization with Validation: Validate incoming JSON before deserialization:
use Symfony\Component\Validator\Validator\ValidatorInterface;
$validator = app(ValidatorInterface::class);
$data = json_decode($json, true);
$errors = $validator->validate($data, new UserConstraints());
if (count($errors) > 0) {
throw new \InvalidArgumentException('Validation failed');
}
$user = $serializer->deserialize($json, User::class, 'json');
Laravel Service Provider:
Bind the serializer to the container in AppServiceProvider:
public function register()
{
$this->app->singleton(SerializerInterface::class, function () {
return SerializerBuilder::create()
->configureHandlers(function (HandlerRegistry $registry) {
$registry->registerSubscribingHandler(new CarbonHandler());
})
->build();
});
}
Middleware for API Responses: Automatically serialize API responses:
namespace App\Http\Middleware;
use Closure;
use JMS\Serializer\SerializerInterface;
class SerializeResponseMiddleware
{
public function __construct(protected SerializerInterface $serializer) {}
public function handle($request, Closure $next)
{
$response = $next($request);
if ($response->getContent() !== '') {
$data = json_decode($response->getContent(), true);
$response->setContent($this->serializer->serialize($data, 'json'));
}
return $response;
}
}
Form Request Validation: Use the serializer to validate and deserialize form data:
use JMS\Serializer\Exception\RuntimeException;
public function rules()
{
return [
'data' => 'required|string',
];
}
public function validated()
{
$data = $this->serializer->deserialize(
$this->input('data'),
User::class,
'json'
);
return $data;
}
Caching Metadata: Improve performance with cached metadata:
$builder->setMetadataCache(new FileLocatorMetadataFactory([
__DIR__.'/var/cache/serializer',
]));
Circular References:
MaxDepthHandler to limit recursion:
$builder->configureHandlers(function (HandlerRegistry $registry) {
$registry->registerSubscribingHandler(new MaxDepthHandler(5));
});
$context = SerializationContext::create()->setEnableMaxDepthChecks(false);
Doctrine Integration:
$entityManager->getRepository(User::class)->find($id); // Initialize proxy
// In User.php
/**
* @Serializer\ExclusionPolicy("all")
* @Serializer\Expose
*/
public $name;
Type Mismatches:
$context = SerializationContext::create()->setStrictMode(true);
DateTimeInterface implementations may fail. Use handlers:
$builder->configureHandlers(function (HandlerRegistry $registry) {
$registry->registerSubscribingHandler(new CustomDateTimeHandler());
});
Annotation Caching:
php artisan cache:clear
var/cache/serializer.XML Namespaces:
$context = SerializationContext::create()
->setXmlRootName('user')
->setXmlNamespace('http://example.com/ns');
Enable Debug Mode:
$builder->setDebug(true);
Logs metadata loading and serialization steps.
Inspect Metadata:
$metadata = $serializer->getMetadataFactory()->getMetadataFor(User::class);
dump($metadata->propertyMetadata);
Handle Exceptions:
Catch RuntimeException for deserialization errors:
try {
$user = $serializer->deserialize($json, User::class, 'json');
} catch (RuntimeException $e) {
Log::error('Deserialization failed: ' . $e->getMessage());
throw new \InvalidArgumentException('Invalid data format');
}
PHP Attributes vs Annotations:
// For PHP 8 attributes
#[Serializer\Expose]
public string $name;
$builder->setMetadataDir(__DIR__.'/metadata');
Custom Metadata: Dynamically add metadata at runtime:
$metadataFactory = MetadataFactory::create();
$metadataFactory->setMetadataCache(new ArrayCollectionMetadataFactory());
$classMetadata = new ClassMetadata(User::class);
$classMetadata->propertyMetadata['email'] = new PropertyMetadata(
'email',
'string',
['groups' => ['admin']]
);
$metadataFactory->setMetadataFor(User::class, $classMetadata);
Event Listeners: Hook into serialization events:
$builder->setEventDispatcher(new EventDispatcher());
$eventDispatcher->
How can I help you explore Laravel packages today?