andrew-gos/serializer
Extensible PHP 8.2+ serializer that normalizes arrays/objects and encodes to JSON or XML. Register custom normalizers and encoders via a configurable Serializer. Pure encoders avoid mutating input and handle XML duplication/circular references.
Installation:
composer require andrew-gos/serializer
Ensure your project uses PHP 8.2+.
First Use Case: Serialize a simple array to JSON:
use AndrewGos\Serializer\SerializerFactory;
$serializer = SerializerFactory::getDefaultSerializer();
$json = $serializer->serialize(['name' => 'John'], 'json');
Where to Look First:
SerializerFactory for default configurations.Encoder\JsonEncoder and Encoder\XmlEncoder for built-in formats.SerializerInterface for custom implementations.Instantiation:
Use SerializerFactory::getDefaultSerializer() for pre-configured serializers with default normalizers (scalars, arrays, objects).
Adding Encoders: Register encoders dynamically:
$serializer->addEncoder('json', new JsonEncoder());
$serializer->addEncoder('xml', new XmlEncoder());
Custom Normalizers: Register type-specific normalizers (e.g., for Eloquent models):
$serializer->addNormalizer(
App\Models\User::class,
fn (App\Models\User $user) => [
'id' => $user->id,
'name' => $user->name,
]
);
Serialization:
$result = $serializer->serialize($data, 'json'); // or 'xml'
JsonEncoder for Laravel API responses (e.g., in App\Http\Resources).XmlEncoder for generating config files or SOAP responses.XmlEncoder (no manual intervention needed).public function handle($request, Closure $next) {
$response = $next($request);
$serializer = app(SerializerInterface::class);
$response->setContent($serializer->serialize($response->getData(), 'json'));
return $response;
}
Nested Objects: Normalize nested objects recursively:
$serializer->addNormalizer(
App\Models\Post::class,
fn (App\Models\Post $post) => [
'title' => $post->title,
'author' => $serializer->serialize($post->author, 'json'),
]
);
Conditional Serialization: Skip properties based on conditions:
$serializer->addNormalizer(
App\Models\User::class,
fn (App\Models\User $user) => [
'email' => $user->email,
'api_token' => $user->api_token ?? null,
]
);
Custom Encoders:
Extend EncoderInterface for new formats (e.g., YAML):
class YamlEncoder implements EncoderInterface {
public function encode($data, array $context = []): string {
return \Spatie\ArrayToXml\ArrayToXml::convert($data);
}
}
Circular References in JSON:
JsonEncoder does not handle circular references by default. Use XmlEncoder or implement a custom normalizer to break cycles:
$serializer->addNormalizer(
stdClass::class,
fn (stdClass $obj) => json_decode(json_encode($obj), true) // Flatten object
);
Array vs. Object Reference Handling:
Normalizer Precedence:
Normalizers are applied in registration order. The last registered normalizer for a type wins. Use hasNormalizer() to check for conflicts:
if (!$serializer->hasNormalizer(stdClass::class)) {
$serializer->addNormalizer(stdClass::class, ...);
}
Performance: Avoid registering normalizers for every possible type in large applications. Group related types (e.g., all Eloquent models) under a base normalizer:
$serializer->addNormalizer(
App\Models\Model::class,
fn (App\Models\Model $model) => $model->toArray()
);
Check Registered Normalizers/Encoders:
$serializer->getNormalizers(); // Array of registered normalizers
$serializer->getEncoders(); // Array of registered encoders
Inspect Serialization Context: Pass a context array to debug normalizer behavior:
$serializer->serialize($data, 'json', ['debug' => true]);
XML Reference Keys:
If XML output is unexpectedly large, verify reference keys are unique. Override XmlEncoder's generateReferenceKey() method:
$encoder = new XmlEncoder();
$encoder->setReferenceKeyGenerator(fn ($data) => spl_object_hash($data));
Custom Context Handling:
Extend SerializerInterface to add context-specific logic:
interface CustomSerializerInterface extends SerializerInterface {
public function serializeWithMetadata($data, string $format, array $context = []): array;
}
Normalizer Factories: Dynamically generate normalizers based on runtime conditions:
$serializer->addNormalizerFactory(
fn (string $class) => $class === App\Models\User::class
? fn (App\Models\User $user) => $user->toArray()
: null
);
Encoder Wrappers: Create wrapper encoders to add headers or modify output:
class JsonApiEncoder implements EncoderInterface {
public function encode($data, array $context = []): string {
return '{"data": ' . (new JsonEncoder())->encode($data) . '}';
}
}
Default Normalizers:
SerializerFactory::getDefaultSerializer() includes normalizers for:
int, string, etc.)array)stdClass and subclasses)
Override these if needed, but test thoroughly.Encoder Format Names:
Encoder format names (e.g., 'json') are case-sensitive. Use lowercase strings.
PHP 8.2 Features: Leverage named arguments in normalizers/encoders for clarity:
$serializer->addNormalizer(
App\Models\Post::class,
fn (App\Models\Post $post) => [
'title' => $post->title,
'published_at' => $post->publishedAt->format('Y-m-d'),
]
);
How can I help you explore Laravel packages today?