从JSON反序列化为PHP,使用强制转换?

2022-08-30 15:39:11

假设我有一个具有“name”和“password”属性的User类,以及一个“save”方法。当通过json_encode将此类的对象序列化为JSON时,该方法被正确跳过,我最终得到类似于{'name':'testName','password':'testPassword'}。

但是,当通过json_decode反序列化时,我最终会得到一个StdClass对象而不是User对象,这是有道理的,但这意味着该对象缺少“save”方法。有没有办法将生成的对象转换为用户,或者向json_decode提供一些提示,以了解我期望的对象类型?


答案 1

老问题,但也许有人会发现这很有用。
我创建了一个带有静态函数的抽象类,您可以在对象上继承该函数,以便将任何 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);

如果 JSON 包含目标对象的数组,则可以使用该方法。::DeserializeArray

下面是一个可运行的示例。


答案 2

简短的回答:不(不是我知道的*)

长答案:json_encode只会序列化公共变量。正如您所看到的,根据JSON规范,没有“函数”数据类型。这些都是方法未序列化到 JSON 对象的原因。

Ryan Graham 是对的 - 将这些对象重新创建为非 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()的输入/输出,但我的测试显示这些是野鹅追逐。


推荐