Сериализовать объекты класса в json формат

101
04 марта 2021, 04:10

Прошу подсказать по учебной задаче.

Требуется модифицировать класс Accessor таким образом, чтобы его объекты можно было сериализовать при помощи serialize() и восстановить при помощи unserialize(). По возможности организовать сериализацию таким образом, чтобы объект сохранялся в JSON-формате.

require_once 'include.php';
$obj = new Accessor();
print_r($obj->jsonSerialize());

Файл include.php:

class Accessor implements JsonSerializable
{
    private $array = ['key1' =>'1','key2' =>'2','key7' =>'7'];
    private $go = 123321;
    public function __construct(array $array) {
        $this->array = $array;
    }
    public function jsonSerialize() {
        return $this->array;
    }
}

Почему jsonSerialize() не отрабатывает?

Answer 1

Давайте внесем некоторую ясность ваш вопрос. Сначала вы пишите про функции стандартной сериализации в пхп - serialize()/unserialize(). Они сохраняют переменную в строку в своем неком формате.
Пример:

class Accessor { 
    private $data = [
                'key1' => 'value1',
                'key2' => 2,
             ];
    private $val = 100;   
}

$a = new Accessor();    
print  serialize( $a );

на выходе получим строку следующего формата (она не совсем корректна, т.к. содержит непечатные символы, которые сюда не копируются)

O:8:"Accessor":2:{s:14:"O:8:"Accessor":2:{s:14:"Accessordata";a:2:{s:4:"key1";s:6:"value1";s:4:"key2";i:2;}s:13:"Accessorval";i:100;}

Данный метод сохранения весьма широко использовался лет так 10 назад, до начала массового использования JSON. Одно из его преимуществ относительно JSON в том, что он содержит информацию о типах. В самом начале идет строка O:8:"Accessor" (O-объект, 8 - длина имени класса, далее название класса). Поэтому с помощью метода unserialize вы получите экземпляр этого же класса.
Недостатком относительно JSON является тот факт, что этот формат не переносим между другими языками. Это внутренний формат для пхп.

А вот JSON напротив, является форматом обмена данных между разными системами но при этом сам по себе не содержит сведений о используемых типах данных.

Эти методы сериализации при вызовах используют "магические" методы объектов __sleep() и __wakeup(), при их наличии в классе объекта, либо если класс поддерживает интерфейс Serializable.
Данный функционал позволяет получить данные объекта, которые следует сериализовать (чтобы не записывать объект целиком), а также выполнить действия по восстановлению состояния после того как объект десериализован.

Далее про кодирование в JSON. Для этих целей используются функции json_encode()/json_decode(). private поля класс не сериализуются. В случае приведенного выше класса, код

print  json_encode( $a );

вернeт "пустой объект" - строку {}. Если же поля будут объявлены как public, то объект будет сериализован в следующую JSON-строку:

{"data":{"key1":"value1","key2":2},"val":100}

Теперь в вашем коде использован интерфейс JsonSerializable. Как и в случаях с serialize() и методами __sleep() и Serializable::serialize этот инетерфейс позволяет получить данные объекта, которые должны быть сохранены. Когда вы хотите сериализовать объект вы все так же вызываете json_encode($a), если объект не поддерживает указанный интерфейс, то происходит сериализация всех доступных полей. Если же переданный объект его поддерживает, то будет вызван метод jsonSerialize() и сериализованы будут только те данные, которые он вернет.

Это позволяет во-первых сериализовать не все подряд, а только нужное, во-вторых - позволяет сериализовать любые поля, которые мы сочтем нужным, поскольку сами формируем массив значений (то есть вручную сохраняем, например, приватные поля).

Поэтому вернув в класс private поля и добавив данный интерфейс:

class Accessor implements JsonSerializable {
    private $data = [
                'key1' => 'value1',
                'key2' => 2,
              ];
    private $val = 100;
    public function jsonSerialize(){
        return $this->data;
    }
}

вызов json_encode($a) вернет нам только значения массива data:

{"key1":"value1","key2":2}

Если нам требуется сохранить значения и $dataи $val, то мы определяем метод соответствующим образом:

public function jsonSerialize(){
    return ['data' => $this->data, 'val' => $this->val];
}

В общем, тут надо понять, что сам метод jsonSerialize ничего не кодирует, а просто предоставляет данные, которые должны быть закодированы.

И возвращаясь в вашему вопросу. Невозможно сказать, как следует модифицировать класс, который указан в задании, поскольку я так понимаю это не его первоначальный вид. Во вторых инструменты serialize() и формат JSON в принципе никак не связаны в пхп. Нельзя добиться, чтобы serialize вдруг вернул json-строку,он возвращает свой формат. Но для этого можно использовать другие инструменты, такие как json_encode.

Answer 2

В зависимости от того, что вы хотите сделать. Если нужно, чтобы возвращался сериализованный массив из свойства array при вызове метода jsonSerialize(), то пропишите в нем:

return json_encode($this->array);

То же самое сделайте для unserialize, только json_decode.

Если же вы хотите, чтобы все работало при вызове именно функции php serialize, то так, увы, не получится, ввиду того, что метод встроенный и алгоритм его работы уже определен.

Есть способ переопределить этот метод через C в движке php. Но я не думаю, что в этом состоит задача.

READ ALSO
Как поправить условия

Как поправить условия

Подскажите как правильно подправить условия? сейчас при заходе на страницу браузер выдает ошибку HTTP ERROR 500

156
Проблема с запросом

Проблема с запросом

Возникла такая проблемаИмеется вот такой код:

86
Получить полный путь

Получить полный путь

Как получить путь к странице с которой сделан переход, типа $_SERVER['HTTP_REFERER'] только нужен именно путь без http://

114
MediaPlayer(FX) вне JavaFX приложения

MediaPlayer(FX) вне JavaFX приложения

Для того, чтобы пользоваться MediaPlayer, нужно использовать его в JavaFX потокеСамая простая программа для запуска JavaFX приложения выглядит примерно...

101