Miałem ostatnio potrzebę serializacji obiektów do formatu JSON. Nie potrzebowałem rozbudowanych bibliotek, a tym bardziej wprowadzania kolejnych zależności do projektu. Mam nieodparte wrażenie, że w każdym z projektów w których pracuje, jesteśmy krok od Dependency Hell.
Na szczęście do prostych zadań, świetnie spisuje się dostarczony w PHP (od wersji 5.4 wraz z modułem JSON) interfejs JsonSerializable. Wystarczy aby klasa implementująca interfejs posiadała metodę jsonSerialize() która zwraca dane mogące zostać zserializowane funkcją json_encode(). Czyli tak na prawdę każdy dowolny typ danych, za wyjątkiem typu resource.
Zdefiniuję na początek trzy różne klasy – Author, Comment, Article. Każda z nich posiada implementację interfejsu JsonSerializable. Dla uproszczenia przykładu w klasie Article, na sztywno ustawiam wymagane wartości.
<?php
class Author implements JsonSerializable
{
private $name;
private $surname;
public function __construct($name, $surname)
{
$this->name = $name;
$this->surname = $surname;
}
public function jsonSerialize()
{
return $this->name . ' ' . $this->surname;
}
}
class Comment implements JsonSerializable
{
private $author;
private $content;
public function __construct(Author $author, $content)
{
$this->author = $author;
$this->content = $content;
}
public function jsonSerialize()
{
return [
'author' => $this->author,
'content' => $this->content
];
}
}
class Article implements JsonSerializable
{
private $id;
private $added;
private $title;
private $author;
private $publicated;
private $comments;
public function __construct()
{
$this->id = 1;
$this->added = new DateTime('-1 day');
$this->title = 'JsonSerializable';
$this->author = new Author('Adrian', 'Pietka');
$this->publicated = true;
$this->comments = [
new Comment(new Author('John', 'Smith'), 'Nice document!')
];
}
public function jsonSerialize()
{
return [
'id' => $this->id,
'added' => $this->added,
'title' => $this->title,
'author' => $this->author,
'publicated' => $this->publicated,
'comments' => $this->comments
];
}
}
Obiekt klasy Author przy serializacji do formatu JSON zwróci najprostszą informację – dana typu string. Natomiast instancja klasy Comment – tablicę asocjacyjną. Najbardziej złożona w tym przypadku jest klasa Article. Jej instancja po serializacji do formatu JSON wymaga również transformacji obiektów zależnych – Author oraz tablicy obiektów Comment.
Sprowadzenie obiektów do formatu JSON wymaga jedynie wywołania funkcji json_encode():
<?php
echo json_encode(new Article(), JSON_PRETTY_PRINT);
Rezultatem wywołania funkcji będzie następujący format danych:
{
"id": 1,
"added": {
"date": "2016-11-29 23:03:11.000000",
"timezone_type": 3,
"timezone": "Europe\/Warsaw"
},
"title": "JsonSerializable",
"author": "Adrian Pietka",
"publicated": true,
"comments": [
{
"author": "John Smith",
"content": "Nice document!"
}
]
}
Można również zmienić domyślny sposób serializacji danych dla klasy DateTime. Wystarczy stwórzyć własną (np. MyDateTime), rozszerzając klasę DateTime, a następnie implementując interfejs JsonSerializable. Potrzeba przykładu? Proszę:
<?php
class MyDateTime extends DateTime implements JsonSerializable
{
public function jsonSerialize()
{
return $this->format('Y-m-d H:i:s');
}
}
Jeżeli Twój system nie składa się z kilkudziesięciu klas które należy zserializować do formatu JSON, rozwiązanie zaprezentowane powyżej jest całkiem przyjemne. Implementacja jednej metody, możliwość definiowania formatu końcowego (struktury oraz nazewnictwa poszczególnych pól) jest szybka, a dodatkowo nie wymaga kolejnej zależności do biblioteki zewnętrznej. Jeśli natomiast nasz system bardzo szybko się rozrasta, posiadamy z dnia na dzień kolejne klasy wymagające transformacji – to prawdopodobnie jest to dobry moment na zastanowienie się nad automatyzacją serializacji.