monkeyscloud / monkeyslegion-serializer
Attribute-driven object↔JSON/XML serializer for MonkeysLegion: property hooks, readonly DTOs, normalizer pipeline, naming strategies, PHPStan Level 9.
Package info
github.com/MonkeysCloud/MonkeysLegion-Serializer
pkg:composer/monkeyscloud/monkeyslegion-serializer
1.0.0
2026-05-17 04:06 UTC
Requires
- php: ^8.4
- ext-dom: *
- ext-json: *
- ext-simplexml: *
Requires (Dev)
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
Suggests
- monkeyscloud/monkeyslegion-cli: ^2.0 — Required for install command
- monkeyscloud/monkeyslegion-di: ^2.0 — Required for SerializerProvider
This package is auto-updated.
Last update: 2026-05-17 04:08:44 UTC
README
Attribute-driven object↔JSON/XML serializer for the MonkeysLegion v2 framework.
PHP 8.4 property hooks • readonly DTOs • backed enums • typed collections • serialization groups • naming strategies • polymorphic discriminators • PHPStan Level 9
Installation
composer require monkeyscloud/monkeyslegion-serializer
Requirements: PHP ≥ 8.4 · ext-json · ext-simplexml · ext-dom
Quick Start
use MonkeysLegion\Serializer\Serializer; use MonkeysLegion\Serializer\Attribute\{Serializable, SerializedName, Ignore, Groups, Type, DateFormat}; #[Serializable] class User { public function __construct( public readonly int $id, public readonly string $name, #[SerializedName('email_address')] public readonly string $email, #[Ignore] public readonly string $passwordHash, #[Groups(['admin'])] public readonly string $role = 'user', #[DateFormat('Y-m-d')] public readonly \DateTimeImmutable $createdAt = new \DateTimeImmutable(), ) {} } $serializer = Serializer::create(); // Serialize to JSON $json = $serializer->toJson(new User( id: 1, name: 'Jorge', email: 'jorge@monkeys.cloud', passwordHash: 'hashed', role: 'admin', )); // {"id":1,"name":"Jorge","email_address":"jorge@monkeys.cloud","role":"admin","created_at":"2026-05-17"} // Deserialize from JSON $user = $serializer->fromJson($json, User::class); echo $user->name; // Jorge
Attributes Reference
| Attribute | Target | Description |
|---|---|---|
#[Serializable] |
Class | Mark class as serializable (optional groups, xmlRoot) |
#[SerializedName('x')] |
Property | Override the output key name |
#[Ignore] |
Property | Exclude from serialization & deserialization |
#[Groups(['api'])] |
Property | Include only when group is active |
#[Type(Item::class)] |
Property | Hint collection item type |
#[DateFormat('Y-m-d')] |
Property | Custom date format |
#[MaxDepth(2)] |
Property | Limit nesting depth for this property |
#[Accessor(getter:, setter:)] |
Property | Custom getter/setter for private properties |
#[Transform(serialize:, deserialize:)] |
Property | Custom value transform callbacks |
#[PreSerialize] |
Method | Hook: runs before serialization |
#[PostDeserialize] |
Method | Hook: runs after deserialization |
#[Discriminator] |
Class | Polymorphic type mapping |
#[XmlRoot('user')] |
Class | XML root element name |
#[XmlElement('item')] |
Property | XML child element name |
#[XmlAttribute] |
Property | Serialize as XML attribute |
Lifecycle Hooks
use MonkeysLegion\Serializer\Attribute\{PreSerialize, PostDeserialize}; class Order { public int $id; public float $subtotal; public float $tax; public float $total = 0; #[PreSerialize] public function computeTotal(): void { $this->total = $this->subtotal + $this->tax; } #[PostDeserialize] public function onLoaded(): void { $this->total = $this->subtotal + $this->tax; } }
Value Transforms
use MonkeysLegion\Serializer\Attribute\Transform; class Contact { #[Transform(serialize: 'strtolower', deserialize: 'strtolower')] public string $email; #[Transform(serialize: 'trim')] public string $name; }
Custom Accessors
use MonkeysLegion\Serializer\Attribute\Accessor; class User { #[Accessor(getter: 'getEmail', setter: 'setEmail')] private string $email; public function getEmail(): string { return $this->email; } public function setEmail(string $v): void { $this->email = strtolower($v); } }
Per-Property Max Depth
use MonkeysLegion\Serializer\Attribute\MaxDepth; class User { public int $id; #[MaxDepth(1)] public ?Company $company; // Only serialized at top-level, not nested }
Exception Handling
use MonkeysLegion\Serializer\Exception\{ SerializerException, SerializationException, DeserializationException, }; try { $user = $serializer->fromJson($invalidJson, User::class); } catch (DeserializationException $e) { // Handle deserialization-specific errors } catch (SerializerException $e) { // Catch all serializer errors }
Serialization Groups
use MonkeysLegion\Serializer\DTO\SerializationContext; // Only include 'api' group fields $json = $serializer->serialize($user, 'json', new SerializationContext( groups: ['api'], ));
Naming Strategies
use MonkeysLegion\Serializer\Enum\NamingStrategy; // camelCase output $serializer = Serializer::create(NamingStrategy::CamelCase); // snake_case output (default) $serializer = Serializer::create(NamingStrategy::SnakeCase); // kebab-case output $serializer = Serializer::create(NamingStrategy::KebabCase);
XML Support
use MonkeysLegion\Serializer\Attribute\{XmlRoot, XmlElement, XmlAttribute}; #[XmlRoot('order')] class Order { #[XmlAttribute] public int $id; #[XmlElement('line_item')] public array $items; } $xml = $serializer->toXml($order);
Backed Enums
enum Status: string { case Active = 'active'; case Inactive = 'inactive'; } class Account { public function __construct( public readonly int $id, public readonly Status $status, ) {} } // Enums serialize to their scalar value automatically $json = $serializer->toJson(new Account(1, Status::Active)); // {"id":1,"status":"active"} // And deserialize back $account = $serializer->fromJson($json, Account::class); $account->status === Status::Active; // true
Polymorphic Discriminator
use MonkeysLegion\Serializer\Attribute\Discriminator; #[Discriminator(field: 'type', map: [ 'cat' => Cat::class, 'dog' => Dog::class, ])] abstract class Animal { public string $name; } class Cat extends Animal { public int $lives = 9; } class Dog extends Animal { public string $breed; } $json = '{"type":"cat","name":"Whiskers","lives":7}'; $animal = $serializer->fromJson($json, Animal::class); // Returns Cat instance
Collections
use MonkeysLegion\Serializer\Attribute\Type; class Order { public int $id; #[Type('list<OrderItem>')] public array $items = []; } // Serialize/deserialize lists $json = $serializer->serializeList($orders); $orders = $serializer->deserializeList($json, Order::class);
Context Options
SerializationContext
| Option | Type | Default | Description |
|---|---|---|---|
groups |
list<string> |
[] |
Active groups (empty = all) |
maxDepth |
int |
10 |
Max object nesting depth |
dateFormat |
string |
ISO 8601 | Default date format |
serializeNulls |
bool |
false |
Include null values |
DeserializationContext
| Option | Type | Default | Description |
|---|---|---|---|
groups |
list<string> |
[] |
Active groups (empty = all) |
ignoreUnknown |
bool |
true |
Skip unknown keys |
dateFormat |
string |
ISO 8601 | Expected date format |
strictTypes |
bool |
false |
Enforce strict coercion |
DI Integration
use MonkeysLegion\Serializer\Provider\SerializerProvider; // Register in your container $serializer = SerializerProvider::register($config['serializer'] ?? []);
Configuration (config/serializer.mlc)
php ml serializer:install
serializer {
default_format = json
naming_strategy = snake_case
serialize_nulls = false
date_format = "Y-m-d\TH:i:sP"
max_depth = 10
json {
pretty_print = false
}
xml {
version = "1.0"
encoding = "UTF-8"
root_tag = "root"
}
}
License
MIT © 2026 MonkeysCloud Team