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.
## Getting Started
### Minimal Setup in Laravel
1. **Installation**:
```bash
composer require jms/serializer-bundle
For Laravel, use the standalone package (no bundle required):
composer require jms/serializer
Basic Serialization:
use JMS\Serializer\SerializerBuilder;
$serializer = SerializerBuilder::create()->build();
$data = ['name' => 'John', 'age' => 30];
$json = $serializer->serialize($data, 'json');
First Use Case: Convert a Laravel Eloquent model to JSON with custom formatting:
$user = User::find(1);
$json = $serializer->serialize($user, 'json', [
'groups' => ['user:minimal'] // Use serialization groups
]);
Where to Look First:
src/JMS/Serializer/ for core classes (e.g., SerializerBuilder, SerializationContext).Serialization Groups:
Use annotations or YAML/XML configs to define groups (e.g., user:minimal, user:detailed) and control output granularity.
// In your model (using annotations)
/**
* @JMS\Expose(groups={"user:minimal"})
*/
private $name;
// In code
$serializer->serialize($user, 'json', ['groups' => ['user:minimal']]);
Handling Eloquent Models:
$builder = SerializerBuilder::create()
->configureMetadataDriver(
new JMS\Serializer\Metadata\Driver\DoctrineAnnotationDriver(
new JMS\Serializer\Metadata\Driver\AnnotationDriver(
new JMS\Serializer\Metadata\Annotation\AnnotationReader()
),
new Doctrine\Common\Annotations\AnnotationReader()
)
);
DateTime, Collection, etc.:
$builder->addHandler('datetime', new CustomDateHandler());
API Responses: Normalize API responses with consistent formatting:
$response = [
'data' => $serializer->serialize($user, 'json', ['groups' => ['user:minimal']]),
'meta' => ['count' => 1]
];
return response()->json($response);
Deserialization: Convert JSON/XML back to objects (e.g., for API requests):
$data = $serializer->deserialize($json, 'App\Models\User', 'json');
Laravel Service Provider: Bind the serializer to the container for dependency injection:
public function register()
{
$this->app->singleton('serializer', function () {
return SerializerBuilder::create()->build();
});
}
Then inject via constructor:
public function __construct(private SerializerInterface $serializer) {}
Middleware for API Responses: Use middleware to auto-serialize Eloquent models in JSON responses:
public function handle($request, Closure $next)
{
$response = $next($request);
if ($response->getContent() instanceof \Illuminate\Database\Eloquent\Model) {
$response->setContent(
$this->serializer->serialize($response->getContent(), 'json')
);
}
return $response;
}
Form Request Validation: Deserialize incoming JSON payloads:
public function rules()
{
$data = $this->serializer->deserialize(
$this->input('data'),
'array',
'json'
);
return [
'data.name' => 'required|string',
// ...
];
}
Circular References:
max_depth in context to limit recursion:
$serializer->serialize($object, 'json', [
'max_depth' => 5
]);
@MaxDepth annotation or custom handlers.Doctrine Proxy Objects:
@ExclusionPolicy or disable proxy handling:
$builder->configureHandlers(function (HandlerRegistry $registry) {
$registry->getSubscribingHandlerInterface('JMS\Serializer\Handler\ProxyHandler')->setProxyInterface(
Doctrine\ORM\PersistentCollection::class
);
});
Type Mismatches:
DateTimeImmutable) may fail. Use custom handlers or configure the builder:
$builder->addHandler('DateTimeImmutable', new DateTimeHandler());
Annotation Caching:
php artisan cache:clear
$builder->configureMetadataDriver(new AnnotationDriver(
new AnnotationReader(),
null, // Disable cache
false
));
XML Namespaces:
$serializer->serialize($object, 'xml', [
'xml_root_node_name' => 'root',
'xml_format_output' => true,
'xml_root_attributes' => ['xmlns' => 'http://example.com']
]);
Enable Verbose Output:
$serializer->serialize($object, 'json', [
'debug' => true
]);
Logs metadata and context during serialization.
Check Metadata: Inspect generated metadata for a class:
$metadata = $serializer->getMetadataFactory()->getMetadataFor(
'App\Models\User'
);
dump($metadata);
Handler Debugging: Override handlers to log input/output:
$builder->addHandler('App\Models\User', new class implements SubscribingHandlerInterface {
public function getSubscribingMethods()
{
return [
new SubscribingMethod('serializeUser', [], 'json', [
new Attribute('groups', ['user:minimal'])
])
];
}
public function serializeUser($visitor, User $user, array $type)
{
\Log::debug('Serializing user', ['user' => $user->toArray()]);
return $visitor->visitString($user->name);
}
});
Custom Handlers: Create handlers for complex types (e.g., Laravel collections, custom value objects):
$builder->addHandler('Illuminate\Support\Collection', new class implements SubscribingHandlerInterface {
public function getSubscribingMethods()
{
return [
new SubscribingMethod('serializeCollection', [], 'json', [
new Attribute('groups', ['collection'])
])
];
}
public function serializeCollection($visitor, Collection $collection, array $type)
{
return $visitor->visitArray($collection->toArray());
}
});
Dynamic Metadata:
Use Metadata\ClassMetadataFactory to dynamically add metadata:
$factory = $serializer->getMetadataFactory();
$metadata = $factory->getMetadataFor('App\Models\User');
$metadata->propertyMetadata['email']->setGroups(['user:minimal']);
Event Listeners: Attach listeners to serialization events:
$serializer->getEventDispatcher()->addListener(
Serializer::EVENT_SERIALIZE_OBJECT_START,
function (ObjectEvent $event) {
if ($event->getObject() instanceof User) {
$event->setData(['custom_field' => 'value']);
}
}
);
PHP Attributes (PHP 8+): Use native attributes for metadata (no annotations needed):
#[JMS\Serializer\Expose(groups: ['user:minimal'])]
private string $name;
Enable in the builder:
$builder->enableAttributes();
Eloquent Relationships:
UndefinedPropertyException. Eager-load or use @MaxDepth:
/**
* @JMS\Expose(maxDepth=1)
*/
public function posts() {}
Carbon Instances:
DateTime before serialization:
$builder->addHandler('Carbon\Carbon', new class implements SubscribingHandlerInterface
How can I help you explore Laravel packages today?