Уничтожение JSON на PHP с помощью кастинга?
Предположим, что у меня есть класс пользователя с свойствами "имя" и "пароль" и метод "сохранить". При сериализации объекта этого класса в JSON через json_encode метод будет правильно пропущен, и я получаю что-то вроде {'name': 'testName', 'password': 'testPassword'}.
Однако при десериализации через json_decode я получаю объект StdClass вместо объекта User, что имеет смысл, но это означает, что объекту не хватает метода "save". Есть ли способ передать результирующий объект в качестве пользователя или предоставить некоторый намек на json_decode относительно того, какой тип объекта я ожидаю?
Ответы
Ответ 1
Я думаю, что лучший способ справиться с этим - через конструктор либо напрямую, либо через factory:
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));
}
}
Ответ 2
Короткий ответ: Нет (не то, что я знаю *)
Длинный ответ: json_encode будет сериализовать общедоступные переменные. Как вы можете видеть в JSON spec, нет типа функции. Это и причины, по которым ваши методы не сериализуются в ваш объект 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(), но мои тесты показали, что это дикие гусиные погони.
Ответ 3
Старый вопрос, но, возможно, кто-то найдет это полезным.
Я создал абстрактный класс со статическими функциями, которые вы можете наследовать на свой объект, чтобы десериализовать любой 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 содержит массив вашего целевого объекта.
Здесь текущий пример.
Ответ 4
Вы можете создать FactoryClass какого-то типа:
function create(array $data)
{
$user = new User();
foreach($data as $k => $v) {
$user->$k = $v;
}
return $user;
}
Это не похоже на решение, которое вы хотели, но оно выполняет вашу работу.
Ответ 5
Взгляните на этот класс, который я написал:
https://github.com/mindplay-dk/jsonfreeze/blob/master/mindplay/jsonfreeze/JsonSerializer.php
Он сохраняет объект-свойство JSON с именем "#type" для хранения имени класса и имеет некоторые ограничения, описанные здесь:
Serialize/unserialize PHP-объект-график для JSON
Ответ 6
Я знаю, что JSON не поддерживает сериализацию функций, что вполне приемлемо и даже желательно. Мои классы в настоящее время используются как объекты ценности при общении с JavaScript, а функции не имеют смысла (и обычные функции сериализации не используются).
Однако, поскольку функциональность, относящаяся к этим классам, увеличивается, инкапсуляция их служебных функций (таких как User save() в этом случае) внутри фактического класса имеет смысл для меня. Это означает, что они больше не являются объектами с высокой стоимостью, и что я столкнулся с этой проблемой.
У моей идеи было бы имя класса, указанное внутри строки JSON, и, вероятно, закончилось бы так:
$string = '{"name": "testUser", "password": "testPassword", "class": "User"}';
$object = json_decode ($string);
$user = ($user->class) $object;
И обратное будет устанавливать свойство класса во время/после json_encode. Я знаю, немного запутанный, но я просто пытаюсь сохранить связанный код вместе. Я, вероятно, в конечном итоге снова извлечу мои служебные функции из классов; модифицированный подход к конструктору кажется немного непрозрачным и сталкивается с проблемами с вложенными объектами.
Я действительно ценю это и любые будущие отзывы.
Ответ 7
Возможно, шаблон hydration
может помочь.
В основном вы создаете новый пустой объект (new User()
), а затем добавляете свойства со значениями из объекта StdClass
. Например, у вас может быть метод hydrate
в User
.
Если возможно в вашем случае, вы можете сделать User
constructor
принять необязательный параметр типа StdClass
и принять значения при создании экземпляра.
Ответ 8
Чтобы ответить на ваш прямой вопрос, нет, не нужно было делать это с помощью 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 класса" ), поэтому вы можете добавить этот код только в одном месте.
Ответ 9
Бит поздно, но другой вариант - использовать сериализатор symfony для десериализации xml, json, независимо от объекта.
здесь есть документация:
http://symfony.com/doc/current/components/serializer.html#deserializing-in-an-existing-object
Ответ 10
Я должен сказать, что я немного встревожен тем, что это не просто стандартная, с полки функциональности - какой-то библиотеки, если не самой JSON. Почему бы вам не захотеть иметь похожие объекты с обеих сторон? (Что касается JSON, вы все равно)
Я что-то упустил? Есть ли библиотека, которая это делает? (AFAICT, ни один из [бережливость, протокольные буферы, avro] на самом деле не имеет API для javascript. Для моей проблемы меня больше всего интересует JS ↔ PHP, несколько также в JS ↔ python.)