Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 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;
}

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

Ответ 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 класса" ), поэтому вы можете добавить этот код только в одном месте.

Ответ 10

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

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