PHPackages                             tcds-io/php-jackson - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. tcds-io/php-jackson

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

tcds-io/php-jackson
===================

A lightweight, flexible object serializer for PHP, inspired by FasterXML/jackson

1.1.0(2mo ago)113.2k↑16.7%[3 issues](https://github.com/tcds-io/php-jackson/issues)5MITPHPPHP &gt;=8.4CI passing

Since Jan 14Pushed 1mo agoCompare

[ Source](https://github.com/tcds-io/php-jackson)[ Packagist](https://packagist.org/packages/tcds-io/php-jackson)[ RSS](/packages/tcds-io-php-jackson/feed)WikiDiscussions main Synced 2d ago

READMEChangelog (3)Dependencies (12)Versions (5)Used By (5)

PHP-Jackson
===========

[](#php-jackson)

[![PHP Tests](https://github.com/tcds-io/php-jackson/actions/workflows/tests.yml/badge.svg)](https://github.com/tcds-io/php-jackson/actions/workflows/tests.yml)

###### A lightweight, flexible object serializer for PHP, inspired by [Jackson](https://github.com/FasterXML/jackson).

[](#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
----------

[](#-contents)

- [Overview](#overview)
- [Integrations](#-integrations)
    - [Laravel ↗](https://github.com/tcds-io/php-jackson-laravel)
    - [Symfony ↗](https://github.com/tcds-io/php-jackson-symfony)
    - [Guzzle ↗](https://github.com/tcds-io/php-jackson-guzzle)
- [Installation](#-installation)
- [Basic Usage](#-basic-usage)
- [Deserializing from JSON](#-deserializing-from-json)
- [Merging Additional Data (`readValueWith`)](#-merging-additional-data-readvaluewith)
- [Serializing Objects](#-serializing-objects)
- [Generic Types (`list  map  shapes`)](#-generic-types-listt-mapkv-shapes)
    - [List Example](#list-example)
    - [Map Example](#map-example)
    - [Array Shape Example](#array-shape-example)
    - [Object Shape Example](#object-shape-example)
- [Renaming JSON keys with `#[JsonProperty]`](#-renaming-json-keys-with-jsonproperty)
- [Custom Type Mappers](#-custom-type-mappers)
    - [Using Custom Mappers with External Context](#using-custom-mappers-with-external-context)
    - [Pinning a Mapper on the Class with `#[JsonMapper]`](#pinning-a-mapper-on-the-class-with-jsonmapper)
- [Date Handling](#-date-handling)
- [Error Handling](#-error-handling)
- [Development](#-development)
- [Summary](#-summary)

---

Overview
--------

[](#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`, `map`, shapes, etc.
- **Date Handling** — built-in support for DateTime and Carbon.
- **Error Reporting** — typed exceptions with full trace paths.

---

🚀 Installation
--------------

[](#-installation)

```
composer require tcds-io/php-jackson
```

🧩 Integrations
--------------

[](#-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:

- [Laravel ↗](https://github.com/tcds-io/php-jackson-laravel) — controller injection, JSON responses, request error handling, and Eloquent casts
- [Symfony ↗](https://github.com/tcds-io/php-jackson-symfony) — controller argument resolvers, JSON responses, and configurable request error handling
- [Guzzle ↗](https://github.com/tcds-io/php-jackson-guzzle) — typed HTTP client with request DTO mapping and async response parsing

🔧 Basic Usage
-------------

[](#-basic-usage)

### Reading JSON into typed objects

[](#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
-------------------------

[](#-deserializing-from-json)

```
$json =  [
            'city' => "Rio de Janeiro",
            'country' => "Brazil",
            'position' => [
                'lat' => -26.9013,
                'lng' => -48.6655,
            ]
        ]
    ]
);
```

---

📤 Serializing Objects
---------------------

[](#-serializing-objects)

Array output:

```
$mapper = new ArrayObjectMapper();
$array = $mapper->writeValue($object);
```

JSON output:

```
$mapper = new JsonObjectMapper();
$json = $mapper->writeValue($object);
```

---

📚 Generic Types (`list`, `map`, shapes)
-----------------------------------------------

[](#-generic-types-listt-mapkv-shapes)

The `generic()` and `shape()` helper functions are loaded by Composer through `php-better-generics`.

### List example

[](#list-example)

```
$list = $mapper->readValue('list', $json);
```

Using `generic()`:

```
$type = generic('list', [LatLng::class]);

$list = $mapper->readValue($type, $json);
```

---

### Map example

[](#map-example)

```
$type = generic('map', ['string', Address::class]);

$result = $mapper->readValue($type, [
    'main'  => Address::mainData(),
    'other' => Address::otherData(),
]);
```

---

### Array Shape Example

[](#array-shape-example)

```
$type = shape('array', [
    'type'     => AccountType::class,
    'position' => LatLng::class,
]);
```

Produces:

```
[
  'type' => AccountType::CHECKING,
  'position' => new LatLng(...),
]
```

---

### Object Shape Example

[](#object-shape-example)

```
$type = shape('object', [
    'type'     => AccountType::class,
    'position' => LatLng::class
]);
```

Produces a `stdClass`:

```
$object->type     === AccountType::CHECKING
$object->position instanceof LatLng
```

---

🏷️ Renaming JSON keys with `#[JsonProperty]`
--------------------------------------------

[](#️-renaming-json-keys-with-jsonproperty)

PHP-Jackson maps JSON keys to PHP names 1:1 by default. When the wire format uses a different naming convention (snake\_case, kebab-case, etc.), pin the JSON key on the constructor parameter (or property) with `#[JsonProperty]`:

```
use Tcds\Io\Jackson\Node\JsonProperty;

readonly class User
{
    public function __construct(
        #[JsonProperty('first_name')] public string $firstName,
        #[JsonProperty('last_name')] public string $lastName,
        public int $age,
    ) {}
}
```

The attribute is honored on **both** directions:

```
$mapper = new JsonObjectMapper();

$user = $mapper->readValue(User::class, '{"first_name":"Arthur","last_name":"Dent","age":42}');
// User { firstName: "Arthur", lastName: "Dent", age: 42 }

$mapper->writeValue($user);
// {"first_name":"Arthur","last_name":"Dent","age":42}
```

Error traces and the `expected` payload on `UnableToParseValue` use the wire key — the one users will recognize from the JSON they are sending — not the PHP identifier.

---

🧩 Custom Type Mappers
---------------------

[](#-custom-type-mappers)

Custom mappers are useful when object construction depends on complex logic or external data:

```
use Tcds\Io\Jackson\ArrayObjectMapper;

$mapper = new ArrayObjectMapper(
    typeMappers: [
        LatLng::class => [
            'reader' => fn(string $data) => new LatLng(...explode(',', $data)),
            'writer' => fn(LatLng $data) => sprintf("%s, %s", $data->lat, $data->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

[](#using-custom-mappers-with-external-context)

```
use Tcds\Io\Jackson\ArrayObjectMapper;

$mapper = new ArrayObjectMapper(
    typeMappers: [
        User::class => [
            'reader' => fn() => Auth::user(),
            'writer' => fn(User $data) => [
                'id' => $data->id,
                'name' => $data->name,
                // 'email' intentionally omitted
            ],
        ]
    ]
);
```

Mapper closures can receive any of the named arguments used internally by PHP-Jackson:

```
fn(mixed $data, string $type, ObjectMapper $mapper, array $path): mixed
```

Use only the parameters you need; `ReflectionFunction::call()` binds them by name.

---

### Pinning a Mapper on the Class with `#[JsonMapper]`

[](#pinning-a-mapper-on-the-class-with-jsonmapper)

If a class always wants the same custom (de)serialization, declare it once on the class itself instead of registering it on every mapper instance:

```
use Tcds\Io\Jackson\Node\JsonMapper;

#[JsonMapper(reader: MoneyReader::class, writer: MoneyWriter::class)]
readonly class Money
{
    public function __construct(public int $cents) {}
}
```

The `reader` and `writer` accept any of:

- a class string of an implementation of `Reader` / `Writer` (instance is built with a no-arg constructor),
- a class string of `StaticReader` / `StaticWriter` (no instance — the static `read` / `write` is called),
- a class string of any class with a matching `__invoke` (treated as a `MapperClosure`),
- an instance of `Reader` / `Writer` (PHP 8.1 `new` in attribute initializers),
- a `Closure` matching `MapperClosure`, when constructing `JsonMapper`programmatically (PHP attribute literals can't carry closures).

**Resolution order on every read/write:**

1. `#[JsonMapper]` attribute on the target class — declaration site, wins
2. `typeMappers` constructor argument
3. default reader/writer

That is, an explicit class-level mapper cannot be silently overridden by mapper-instance config — the class itself is the canonical source.

---

🕒 Date Handling
---------------

[](#-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
----------------

[](#-error-handling)

When parsing fails, the library throws:

### `UnableToParseValue`

[](#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.

---

🔧 Development
-------------

[](#-development)

```
composer install
composer tests       # runs cs:check + phpstan + phpunit
composer cs:fix      # auto-fix code style
```

---

✅ Summary
---------

[](#-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`, `map`, shapes)
- Rename wire keys per field with `#[JsonProperty('snake_case')]`
- Register custom mappers for any class via `typeMappers` or pin them on the class itself with `#[JsonMapper(reader: …, writer: …)]`
- Rely on strong error tracing with full path information

###  Health Score

48

—

FairBetter than 93% of packages

Maintenance82

Actively maintained with recent releases

Popularity28

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~55 days

Total

3

Last Release

62d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/c8ac1821d71c0ddddcc3494a165dc40cfa3e759a467d8a00956ceeae4b833bb3?d=identicon)[thiagocordeiro](/maintainers/thiagocordeiro)

---

Top Contributors

[![thiagocordeiro](https://avatars.githubusercontent.com/u/1073649?v=4)](https://github.com/thiagocordeiro "thiagocordeiro (71 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/tcds-io-php-jackson/health.svg)

```
[![Health](https://phpackages.com/badges/tcds-io-php-jackson/health.svg)](https://phpackages.com/packages/tcds-io-php-jackson)
```

###  Alternatives

[mck89/peast

Peast is PHP library that generates AST for JavaScript code

19139.2M47](/packages/mck89-peast)[sauladam/shipment-tracker

Parses tracking information for several carriers, like UPS, USPS, DHL and GLS by simply scraping the data. No need for any kind of API access.

9843.5k](/packages/sauladam-shipment-tracker)[jstewmc/rtf

Read and write Rich Text Format (RTF) documents with PHP

45153.1k6](/packages/jstewmc-rtf)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
