Уничтожение JSON на PHP с помощью кастинга?

Предположим, у меня есть класс User с свойствами «name» и «password» и «save». При сериализации объекта этого класса в JSON через json_encode метод будет правильно пропущен, и я получаю что-то вроде {'name': 'testName', 'password': 'testPassword'}.

Однако при десериализации через json_decode я получаю объект StdClass вместо объекта User, что имеет смысл, но это означает, что объект не имеет метода «save». Есть ли способ передать результирующий объект в качестве пользователя или предоставить некоторый намек на json_decode относительно того, какой тип объекта я ожидаю?

Короткий ответ: Нет (не то, что я знаю *)

Длинный ответ: json_encode будет сериализовать общедоступные переменные. Как вы можете видеть по спецификации JSON , нет типа данных «функции». Это и причины, по которым ваши методы не сериализуются в ваш объект JSON.

Райан Грэм прав – единственный способ воссоздать эти объекты, поскольку экземпляры non-stdClass – это повторное создание их после десериализации.

пример

<?php class Person { public $firstName; public $lastName; public function __construct( $firstName, $lastName ) { $this->firstName = $firstName; $this->lastName = $lastName; } public static function createFromJson( $jsonString ) { $object = json_decode( $jsonString ); return new self( $object->firstName, $object->lastName ); } public function getName() { return $this->firstName . ' ' . $this->lastName; } } $p = new Person( 'Peter', 'Bailey' ); $jsonPerson = json_encode( $p ); $reconstructedPerson = Person::createFromJson( $jsonPerson ); echo $reconstructedPerson->getName(); 

Кроме того, если вам действительно не нужны данные как JSON, вы можете просто использовать обычную сериализацию и использовать крючки __sleep () и __wakeup () для достижения дополнительной настройки.

* В предыдущем вопросе о моей работе было предложено реализовать некоторые из SPL-интерфейсов для настройки ввода / вывода json_encode (), но мои тесты показали, что это дикие гусиные погони.

Я думаю, что лучший способ справиться с этим – через конструктор либо напрямую, либо через фабрику:

 class User { public $username; public $nestedObj; //another class that has a constructor for handling json ... // This could be make private if the factories below are used exclusively // and then make more sane constructors like: // __construct($username, $password) public function __construct($mixed) { if (is_object($mixed)) { if (isset($mixed->username)) $this->username = $mixed->username; if (isset($mixed->nestedObj) && is_object($mixed->nestedObj)) $this->nestedObj = new NestedObject($mixed->nestedObj); ... } else if (is_array($mixed)) { if (isset($mixed['username'])) $this->username = $mixed['username']; if (isset($mixed['nestedObj']) && is_array($mixed['nestedObj'])) $this->nestedObj = new NestedObj($mixed['nestedObj']); ... } } ... public static fromJSON_by_obj($json) { return new self(json_decode($json)); } public static fromJSON_by_ary($json) { return new self(json_decode($json, TRUE)); } } 

Старый вопрос, но, может быть, кто-то найдет это полезным.
Я создал абстрактный класс со статическими функциями, которые вы можете наследовать на свой объект, чтобы десериализовать любой JSON в экземпляре класса наследования.

 abstract class JsonDeserializer { /** * @param string|array $json * @return $this */ public static function Deserialize($json) { $className = get_called_class(); $classInstance = new $className(); if (is_string($json)) $json = json_decode($json); foreach ($json as $key => $value) { if (!property_exists($classInstance, $key)) continue; $classInstance->{$key} = $value; } return $classInstance; } /** * @param string $json * @return $this[] */ public static function DeserializeArray($json) { $json = json_decode($json); $items = []; foreach ($json as $item) $items[] = self::Deserialize($item); return $items; } } 

Вы используете его, наследуя его в классе, который имеет значения, которые будет иметь ваш JSON:

 class MyObject extends JsonDeserializer { /** @var string */ public $property1; /** @var string */ public $property2; /** @var string */ public $property3; /** @var array */ public $array1; } 

Пример использования:

 $objectInstance = new MyObject(); $objectInstance->property1 = 'Value 1'; $objectInstance->property2 = 'Value 2'; $objectInstance->property3 = 'Value 3'; $objectInstance->array1 = ['Key 1' => 'Value 1', 'Key 2' => 'Value 2']; $jsonSerialized = json_encode($objectInstance); $deserializedInstance = MyObject::Deserialize($jsonSerialized); 

Вы можете использовать метод ::DeserializeArray если ваш JSON содержит массив вашего целевого объекта.

Вот прогоняемый образец.

Вы можете создать FactoryClass:

 function create(array $data) { $user = new User(); foreach($data as $k => $v) { $user->$k = $v; } return $user; } 

Это не похоже на решение, которое вы хотели, но оно выполняет вашу работу.

Взгляните на этот класс, я написал:

https://github.com/mindplay-dk/jsonfreeze/blob/master/mindplay/jsonfreeze/JsonSerializer.php

Он сохраняет объект-свойство JSON с именем «#type» для хранения имени класса и имеет некоторые ограничения, которые описаны здесь:

Сериализовать / unserialize PHP-объект-график для JSON

Я знаю, что JSON не поддерживает сериализацию функций, что вполне приемлемо и даже желательно. Мои классы в настоящее время используются как объекты ценности при общении с JavaScript, а функции не имеют смысла (а обычные функции сериализации не используются).

Однако, поскольку функциональность, относящаяся к этим классам, увеличивается, инкапсуляция их служебных функций (таких как сохранение пользователя () в этом случае) внутри фактического класса имеет смысл для меня. Это означает, что они больше не являются объектами с высокой стоимостью, и именно здесь я сталкиваюсь с вышеупомянутой проблемой.

Идея, которая у меня была, будет иметь имя класса, указанное внутри строки JSON, и, вероятно, закончится следующим образом:

 $string = '{"name": "testUser", "password": "testPassword", "class": "User"}'; $object = json_decode ($string); $user = ($user->class) $object; 

И обратное будет устанавливать свойство класса во время / после json_encode. Я знаю, немного запутанный, но я просто пытаюсь сохранить связанный код вместе. Я, вероятно, в конечном итоге снова извлечу мои служебные функции из классов; подход модифицированного конструктора кажется немного непрозрачным и сталкивается с проблемами с вложенными объектами.

Однако я ценю это и любые будущие отзывы.

Возможно, hydration может помочь.

В основном вы создаете новый пустой объект ( new User() ), а затем вы заполняете свойства значениями из объекта StdClass . Например, у вас может быть метод hydrate в User .

Если возможно, в вашем случае вы можете заставить constructor User принять необязательный параметр типа StdClass и принять значения при создании экземпляра.

Чтобы ответить на ваш прямой вопрос, нет, не нужно было делать это с помощью json_encode / json_decode. JSON был разработан и определен как формат для кодирования информации, а не для сериализации объектов. Функция PHP не выходит за рамки этого.

Если вы заинтересованы в воссоздании объектов из JSON, одним из возможных решений является статический метод для всех объектов вашей иерархии, которые принимают stdClass / string и заполняют переменные, которые выглядят примерно так:

 //semi pseudo code, not tested static public function createFromJson($json){ //if you pass in a string, decode it to an object $json = is_string($json) ? json_decode($json) : $json; foreach($json as $key=>$value){ $object = new self(); if(is_object($value)){ $object->{$key} = parent::createFromJson($json); } else{ $object->{$key} = $value; } } return $object; } 

Я не тестировал это, но я надеюсь, что это передумает. В идеале, все ваши объекты должны простираться от какого-то базового объекта (обычно называемого «Object класса»), поэтому вы можете добавить этот код только в одном месте.

Бит поздно, но другой вариант – использовать сериализатор символов для десериализации xml, json, независимо от объекта.

здесь есть документация: http://symfony.com/doc/current/components/serializer.html#deserializing-in-an-existing-object

Должен сказать, что я немного смущен, что это не просто стандартная, с полными функциональными возможностями – какой-то библиотеки, если не самой JSON. Почему бы вам не захотеть иметь похожие объекты с обеих сторон? (Что же касается JSON, то все равно)

Я что-то упустил? Есть ли библиотека, которая это делает? (AFAICT, ни один из [бережливых, протокольных буферов, avro] на самом деле не имеет API для javascript. Для моей проблемы меня больше всего интересует JS <-> PHP, несколько также в JS <-> python.)