# Custom Serialization JSON documents are sent to and received from both PostgreSQL and SQLite as `string`s; the translation to and from PHP classes uses the built-in `json_encode` and `json_decode` functions by default. These do an acceptable job, particularly if you do not care much about how the document is represented. If you want more control, though, there is a library that can help. ## Using `square/pjson` to Control Serialization The [`square/pjson`][pjson] library provides an attribute, a trait, and a few interfaces. If these interfaces are implemented on your classes, `PDODocument` will use them instead of the standard serialization functions. This will not be an exhaustive tutorial of the library, but a few high points: - Properties with the `#[Json]` attribute will be de/serialized, whether `private`, `protected`, or `public`; properties without an annotation will be ignored. - `use JsonSerialize;` brings in the trait that implements pjson's behavior; this should be present in each class. - Array properties must include the type in the attribute, so the library knows how to handle the object. - A strongly-typed class that is represented by a single JSON value can be wired in by implementing `toJsonData` and `fromJsonData`. - The library will not use the class's constructor to create its instances; default values in constructor-promoted properties will not be present if they are not specifically included in the document. An example will help here; we will demonstrate all of the above. ```php use Square\Pjson\{Json, JsonDataSerializable, JsonSerialize}; // A strongly-typed ID for our things; it is stored as a string, but wrapped in this class class ThingId implements JsonDataSerializable { public string $value = ''; public function __construct(string $value = '') { $this->value = $value; } public function toJsonData(): string { return $this->value; } // $jd = JSON data public static function fromJsonData(mixed $jd, array|string $path = []): static { return new static($jd); } } // A thing; note the JsonSerialize trait class Thing { use JsonSerialize; #[Json] public ThingId $id; #[Json] public string $name = ''; // If the property is null, it will not be part of the JSON document #[Json(omit_empty: true)] public ?string $notes = null; public function __construct() { $this->id = new ThingId(); } } class BoxOfThings { use JsonSerialize; // Customize the JSON key for this field #[Json('box_number')] public int $boxNumber = 0; // Specify the type of this array #[Json(type: Thing::class)] public array $things = []; } ``` With these declarations, the following code... ```php $thing1 = new Thing(); $thing1->id = new ThingId('one'); $thing1->name = 'The First Thing'; $thing2 = new Thing(); $thing2->id = new ThingId('dos'); $thing2->name = 'Alternate'; $thing2->notes = 'spare'; $box = new BoxOfThings(); $box->boxNumber = 6; $box->things = [$thing1, $thing2]; echo $box->toJsonString(); ``` ...will produce this JSON: _(manually pretty-printed below)_ ```json { "box_number": 6, "things": [ { "id": "one", "name": "The First Thing" }, { "id": "dos", "name": "Alternate", "notes": "spare" } ] } ``` Deserializing that tree, we get: ```php $box2 = BoxOfThings::fromJsonString('...'); var_dump($box2); ``` ``` object(BoxOfThings)#13 (2) { ["boxNumber"]=> int(6) ["things"]=> array(2) { [0]=> object(Thing)#25 (3) { ["id"]=> object(ThingId)#29 (1) { ["value"]=> string(3) "one" } ["name"]=> string(15) "The First Thing" ["notes"]=> NULL } [1]=> object(Thing)#28 (3) { ["id"]=> object(ThingId)#31 (1) { ["value"]=> string(3) "dos" } ["name"]=> string(9) "Alternate" ["notes"]=> string(5) "spare" } } } ``` Our round-trip was successful! Any object passed as a document will use `square/pjson` serialization if it is defined. When passing an array as a document, if that array only has one value, and that value implements `square/pjson` serialization, it will be used; otherwise, arrays will use the standard `json_encode`/`json_decode` pairs. In practice, the ability to call `Patch::by*` with an array of fields to be updated may not give the results you would expect; using `Document::update` will replace the entire document, but it will use the `square/pjson` serialization. ## Uses for Custom Serialization - If you exchange JSON documents with a data partner, they may have a specific format they provide or expect; this allows you to work with objects rather than trees of parsed JSON. - If you use DDD to define custom types (similar to the `ThingId` example above), you can implement converters to translate them to/from your preferred JSON representation. [pjson]: https://github.com/square/pjson "PJson • Square • GitHub"