tcds-io / php-jackson
A lightweight, flexible object serializer for PHP, inspired by FasterXML/jackson
Installs: 47
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 3
pkg:composer/tcds-io/php-jackson
Requires
- php: >=8.4
- ext-json: *
- tcds-io/php-better-generics: dev-main
Requires (Dev)
- nesbot/carbon: ^3.10
- php-cs-fixer/shim: ^3.88
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.3
- symfony/var-dumper: ^6.0
This package is auto-updated.
Last update: 2025-11-18 09:30:44 UTC
README
A lightweight, flexible object serializer for PHP, inspired by Jackson.
It provides strong typing, JSON ↔ object mapping, generics support, array/object shapes, custom type mappers, and detailed error tracing.
📚 Contents
- Overview
- Integrations
- Installation
- Basic Usage
- Deserializing from JSON
- Merging Additional Data (
readValueWith) - Serializing Objects
- Generic Types (
list<t> map<k, v> shapes) - Custom Type Mappers
- Date Handling
- Error Handling
- Summary
Overview
Main components:
- JsonObjectMapper — handles JSON strings at the boundary.
- ArrayObjectMapper — handles associative arrays at the boundary.
- Type Mappers — custom readers/writers for specific classes.
- Generic Types — support for
list<T>,map<K,V>, shapes, etc. - Date Handling — built-in support for DateTime and Carbon.
- Error Reporting — typed exceptions with full trace paths.
🚀 Installation
composer require tcds-io/php-jackson
🧩 Integrations
PHP Jackson offers first-class integrations for popular PHP frameworks and tools. Each integration extends the core mapper with framework-specific features for a smoother development experience.
Official Plugins:
🔧 Basic Usage
Reading JSON into typed objects
use Tcds\Io\Jackson\JsonObjectMapper; $mapper = new JsonObjectMapper(); $address = $mapper->readValue(Address::class, $json);
Equivalent array version:
use Tcds\Io\Jackson\ArrayObjectMapper; $mapper = new ArrayObjectMapper(); $address = $mapper->readValue(Address::class, $dataArray);
📥 Deserializing from JSON
$json = <<<JSON { "street": "Ocean avenue", "number": "100", "main": "true", "place": { "city": "Rio de Janeiro", "country": "Brazil", "position": { "lat": "-26.9013", "lng": "-48.6655" } } } JSON; $mapper = new JsonObjectMapper(); $address = $mapper->readValue(Address::class, $json);
The resulting object matches:
new Address( street: 'Ocean avenue', number: 100, main: true, place: new Place( city: 'Rio de Janeiro', country: 'Brazil', position: new LatLng(lat: -26.9013, lng: -48.6655), ), );
➕ Merging Additional Data (readValueWith)
Merging data is useful when the incoming payload does not contain all required values and those values must be completed from another source:
$partial = <<<JSON { "street": "Ocean avenue", "number": "100", "main": "true" } JSON; $address = $mapper->readValueWith( Address::class, $partial, [ 'place' => [ 'city' => "Rio de Janeiro", 'country' => "Brazil", 'position' => [ 'lat' => -26.9013, 'lng' => -48.6655, ] ] ] );
📤 Serializing Objects
Array output:
$mapper = new ArrayObjectMapper(); $array = $mapper->writeValue($object);
JSON output:
$mapper = new JsonObjectMapper(); $json = $mapper->writeValue($object);
📚 Generic Types (list<T>, map<K,V>, shapes)
List example
$list = $mapper->readValue('list<LatLng>', $json);
Using generic():
$type = generic('list', [LatLng::class]); $list = $mapper->readValue($type, $json);
Map example
$type = generic('map', ['string', Address::class]); $result = $mapper->readValue($type, [ 'main' => Address::mainData(), 'other' => Address::otherData(), ]);
Array Shape Example
$type = shape('array', [ 'type' => AccountType::class, 'position' => LatLng::class, ]);
Produces:
[ 'type' => AccountType::CHECKING, 'position' => new LatLng(...), ]
Object Shape Example
$type = shape('object', [ 'type' => AccountType::class, 'position' => LatLng::class ]);
Produces a stdClass:
$object->type === AccountType::CHECKING $object->position instanceof LatLng
🧩 Custom Type Mappers
Custom mappers are useful when object construction depends on complex logic or external data:
$mapper = new ArrayObjectMapper( typeMappers: [ LatLng::class => [ 'reader' => fn (string $value) => new LatLng(...explode(',', $value)), 'writer' => fn (LatLng $value) => sprintf("%s, %s", $value->lat, $value->lng), ] ] );
This allows:
"position" => "-26.9013, -48.6655"
to become:
new LatLng(-26.9013, -48.6655)
and serialize back into:
"position" => "-26.9013, -48.6655"
Using Custom Mappers with External Context
$mapper = new ArrayObjectMapper( typeMappers: [ User::class => [ 'reader' => fn () => Auth::user(), 'writer' => fn (User $user) => [ 'id' => $user->id, 'name' => $user->name, // 'email' intentionally omitted ], ] ] );
🕒 Date Handling
PHP-Jackson provides built-in support for:
- DateTime
- DateTimeImmutable
- Carbon
- CarbonImmutable
- DateTimeInterface
Dates are serialized and deserialized using ISO-8601 strings:
[ 'datetime' => '2025-10-22T11:21:31+00:00' ]
❗ Error Handling
When parsing fails, the library throws:
UnableToParseValue
Properties:
$e->trace; // ['address','place','position'] $e->expected; // expected type description $e->given; // actual given value
Example message:
Unable to parse value at .address.place.position
This makes debugging extremely easy.
✅ Summary
You can:
- Read JSON → typed objects via
JsonObjectMapper - Read arrays → typed objects via
ArrayObjectMapper - Merge missing fields using
readValueWith - Write objects → JSON/arrays via
writeValue - Use generics (
list<T>,map<K,V>, shapes) - Register custom mappers for any class
- Rely on strong error tracing with full path information