Skip to content

MonkeysCloud/MonkeysLegion-Serializer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MonkeysLegion Serializer

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

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages