json_decode 到自定义类

发布于 2024-10-24 17:35:51 字数 40 浏览 2 评论 0 原文

是否可以将 json 字符串解码为 stdClass 以外的对象?

Is it possible to decode a json string to an object other than stdClass?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

烟花肆意 2024-10-31 17:35:52

您可以通过以下方式做到这一点..

<?php
class CatalogProduct
{
    public $product_id;
    public $sku;
    public $name;
    public $set;
    public $type;
    public $category_ids;
    public $website_ids;

    function __construct(array $data) 
    {
        foreach($data as $key => $val)
        {
            if(property_exists(__CLASS__,$key))
            {
                $this->$key =  $val;
            }
        }
    }
}

?>

欲了解更多详情,请访问
create-custom-class-in-php-from- json 或数组

You can do it in below way ..

<?php
class CatalogProduct
{
    public $product_id;
    public $sku;
    public $name;
    public $set;
    public $type;
    public $category_ids;
    public $website_ids;

    function __construct(array $data) 
    {
        foreach($data as $key => $val)
        {
            if(property_exists(__CLASS__,$key))
            {
                $this->$key =  $val;
            }
        }
    }
}

?>

For more details visit
create-custom-class-in-php-from-json-or-array

み青杉依旧 2024-10-31 17:35:52

您可以为您的对象创建一个包装器,并使包装器看起来就像对象本身一样。它将适用于多级对象。

<?php
class Obj
{
    public $slave;

    public function __get($key) {
        return property_exists ( $this->slave ,  $key ) ? $this->slave->{$key} : null;
    }

    public function __construct(stdClass $slave)
    {
        $this->slave = $slave;
    }
}

$std = json_decode('{"s3":{"s2":{"s1":777}}}');

$o = new Obj($std);

echo $o->s3->s2->s1; // you will have 777

You can make a wrapper for your object and make the wrapper look like it is the object itself. And it will work with multilevel objects.

<?php
class Obj
{
    public $slave;

    public function __get($key) {
        return property_exists ( $this->slave ,  $key ) ? $this->slave->{$key} : null;
    }

    public function __construct(stdClass $slave)
    {
        $this->slave = $slave;
    }
}

$std = json_decode('{"s3":{"s2":{"s1":777}}}');

$o = new Obj($std);

echo $o->s3->s2->s1; // you will have 777
请你别敷衍 2024-10-31 17:35:52

使用 反射

function json_decode_object(string $json, string $class)
{
    $reflection = new ReflectionClass($class);
    $instance = $reflection->newInstanceWithoutConstructor();
    $json = json_decode($json, true);
    $properties = $reflection->getProperties();
    foreach ($properties as $key => $property) {
        $property->setAccessible(true);
        $property->setValue($instance, $json[$property->getName()]);
    }
    return $instance;
}

Use Reflection:

function json_decode_object(string $json, string $class)
{
    $reflection = new ReflectionClass($class);
    $instance = $reflection->newInstanceWithoutConstructor();
    $json = json_decode($json, true);
    $properties = $reflection->getProperties();
    foreach ($properties as $key => $property) {
        $property->setAccessible(true);
        $property->setValue($instance, $json[$property->getName()]);
    }
    return $instance;
}
戏剧牡丹亭 2024-10-31 17:35:52

不,从 PHP 5.5.1 开始这是不可能的。

唯一可能的是 json_decode返回关联数组而不是 StdClass 对象。

No, this is not possible as of PHP 5.5.1.

The only thing possible is to have json_decode return associate arrays instead of the StdClass objects.

清风夜微凉 2024-10-31 17:35:52

正如戈登所说,这是不可能的。但是,如果您正在寻找一种方法来获取可以解码为给定类实例的字符串,您可以使用 序列化 并反序列化。

class Foo
{

    protected $bar = 'Hello World';

    function getBar() {
        return $this->bar;
    }

}

$string = serialize(new Foo);

$foo = unserialize($string);
echo $foo->getBar();

As Gordon says is not possible. But if you are looking for a way to obtain a string that can be decoded as an instance of a give class you can use serialize and unserialize instead.

class Foo
{

    protected $bar = 'Hello World';

    function getBar() {
        return $this->bar;
    }

}

$string = serialize(new Foo);

$foo = unserialize($string);
echo $foo->getBar();
分分钟 2024-10-31 17:35:52

我曾经为此目的创建了一个抽象基类。我们将其称为 JsonConvertible。它应该对公共成员进行序列化和反序列化。使用反射和后期静态绑定可以实现这一点。

abstract class JsonConvertible {
   static function fromJson($json) {
       $result = new static();
       $objJson = json_decode($json);
       $class = new \ReflectionClass($result);
       $publicProps = $class->getProperties(\ReflectionProperty::IS_PUBLIC);
       foreach ($publicProps as $prop) {
            $propName = $prop->name;
            if (isset($objJson->$propName) {
                $prop->setValue($result, $objJson->$propName);
            }
            else {
                $prop->setValue($result, null);
            }
       }
       return $result;
   }
   function toJson() {
      return json_encode($this);
   }
} 

class MyClass extends JsonConvertible {
   public $name;
   public $whatever;
}
$mine = MyClass::fromJson('{"name": "My Name", "whatever": "Whatever"}');
echo $mine->toJson();

只是凭记忆,所以可能并不完美。您还必须排除静态属性,并且可能会为派生类提供在序列化到 json 或从 json 序列化时忽略某些属性的机会。尽管如此,我希望你能明白。

I once created an abstract base class for this purpose. Let's call it JsonConvertible. It should serialize and deserialize the public members. This is possible using Reflection and late static binding.

abstract class JsonConvertible {
   static function fromJson($json) {
       $result = new static();
       $objJson = json_decode($json);
       $class = new \ReflectionClass($result);
       $publicProps = $class->getProperties(\ReflectionProperty::IS_PUBLIC);
       foreach ($publicProps as $prop) {
            $propName = $prop->name;
            if (isset($objJson->$propName) {
                $prop->setValue($result, $objJson->$propName);
            }
            else {
                $prop->setValue($result, null);
            }
       }
       return $result;
   }
   function toJson() {
      return json_encode($this);
   }
} 

class MyClass extends JsonConvertible {
   public $name;
   public $whatever;
}
$mine = MyClass::fromJson('{"name": "My Name", "whatever": "Whatever"}');
echo $mine->toJson();

Just from memory, so probably not flawless. You will also have to exclude static properties and may give derived classes the chance to make some properties ignored when serialized to/from json. I hope you get the idea, nonetheless.

怎言笑 2024-10-31 17:35:52

JSON 是一种在各种编程语言之间传输数据的简单协议(它也是 JavaScript 的子集),仅支持某些类型:数字、字符串、数组/列表、对象/字典。对象只是键=值映射,数组是有序列表。

所以没有办法以通用的方式表达自定义对象。解决方案是定义一个结构,您的程序将知道它是一个自定义对象。

这是一个示例:

{ "cls": "MyClass", fields: { "a": 123, "foo": "bar" } }

这可用于创建 MyClass 的实例并将字段 afoo 设置为 123“栏”

JSON is a simple protocol to transfer data between various programming languages (and it's also a subset of JavaScript) which supports just certain types: numbers, strings, arrays/lists, objects/dicts. Objects are just key=value maps and Arrays are ordered lists.

So there is no way to express custom objects in a generic way. The solution is defining a structure where your program(s) will know that it's a custom object.

Here's an example:

{ "cls": "MyClass", fields: { "a": 123, "foo": "bar" } }

This could be used to create an instance of MyClass and set the fields a and foo to 123 and "bar".

℡Ms空城旧梦 2024-10-31 17:35:52

我继续将 John Petit 的答案实现为一个函数(gist):

function json_decode_to(string $json, string $class = stdClass::class, int $depth = 512, int $options = 0)
{
    $stdObj = json_decode($json, false, $depth, $options);
    if ($class === stdClass::class) return $stdObj;

    $count = strlen($class);
    $temp = serialize($stdObj);
    $temp = preg_replace("@^O:8:\"stdClass\":@", "O:$count:\"$class\":", $temp);
    return unserialize($temp);  
}

这非常适合我的用例。然而,Yevgeniy Afanasyev 的回应对我来说似乎同样有希望。可以让你的类有一个额外的“构造函数”,如下所示:

public static function withJson(string $json) {
    $instance = new static();
    // Do your thing
    return $instance;
}

这也是受到这个答案的启发。

编辑:我已经使用 karriereat/json-decoder 一段时间了,我已经绝对没问题。它是轻量级的并且非常容易扩展。 这是我编写的用于将 JSON 反序列化为 Carbon/CarbonImmutable 对象的绑定示例

I went ahead and implemented John Petit's answer, as a function(gist):

function json_decode_to(string $json, string $class = stdClass::class, int $depth = 512, int $options = 0)
{
    $stdObj = json_decode($json, false, $depth, $options);
    if ($class === stdClass::class) return $stdObj;

    $count = strlen($class);
    $temp = serialize($stdObj);
    $temp = preg_replace("@^O:8:\"stdClass\":@", "O:$count:\"$class\":", $temp);
    return unserialize($temp);  
}

This worked perfectly for my use case. However Yevgeniy Afanasyev's response seems equally promising to me. It could be possible to have your class have an extra "constructor", like so:

public static function withJson(string $json) {
    $instance = new static();
    // Do your thing
    return $instance;
}

This is also inspired by this answer.

EDIT: I have been using karriereat/json-decoder for some time now, and I have had absolutely no trouble with it. It is lightweight and very easily extensible. Here's an example of a binding I wrote to deserialize JSON into a Carbon/CarbonImmutable object.

守护在此方 2024-10-31 17:35:52

这对我有用,特别是如果目标类中没有设置器或命名属性

function cast($jsonstring, $class)
{
   //$class is a string like 'User'

    $json= json_decode($jsonstring,true);  //array
   
    $reflection = new ReflectionClass($class);
    $instance = $reflection->newInstanceWithoutConstructor();
    $keys = array_keys($json);

    foreach ($keys  as $key => $property) {
        $instance->{$property} =$json[$property];
    }

   // print_r($instance);

    return $instance;
}

This worked for me, especially for if you don't have setters or named properties in the target class

function cast($jsonstring, $class)
{
   //$class is a string like 'User'

    $json= json_decode($jsonstring,true);  //array
   
    $reflection = new ReflectionClass($class);
    $instance = $reflection->newInstanceWithoutConstructor();
    $keys = array_keys($json);

    foreach ($keys  as $key => $property) {
        $instance->{$property} =$json[$property];
    }

   // print_r($instance);

    return $instance;
}

下壹個目標 2024-10-31 17:35:52

不直接,但如果类有一个参数名称与 JSON 对象中的键匹配的构造函数,您可以简单地将 JSON 解码为关联数组,然后通过“...”(参数解包)运算符将其传递给构造函数:

<?php
class MyClass {
    public function __construct(
        public int $id,
        public string $name,
        public array $attributes,
    ){}
}
$json = '{"name":"foo","id":42,"attributes":{"color":"red"}}';
$object = new MyClass(...json_decode($json, true));
print_r($object);

输出:

MyClass Object
(
    [id] => 42
    [name] => foo
    [attributes] => Array
        (
            [color] => red
        )

)

然而,在实践中,经常需要做一些额外的映射,特别是也需要递归解码的子对象。因此,通常最好在每个类中有一个静态 fromArray 函数,在将结果传递给构造函数之前预处理 json 解码的数组:

class Part {
    public function __construct(public float $weight){}
    public static function fromArray(array $data): self {
        return new self(...$data);
    }
}
class System {
    public function __construct(
        public string $name,
        public Part $mainPart,
        public array $otherParts,
    ){}
    public static function fromArray(array $data): self {
        $data['mainPart'] = Part::fromArray($data['mainPart']);
        $data['otherParts'] = array_map(Part::fromArray(...), $data['otherParts']); // php 8.1
        return new self(...$data);
    }
}
$json = '{"name":"foo","mainPart":{"weight":2},"otherParts":[{"weight":1}, {"weight":0.5}]}';
$object = System::fromArray(json_decode($json, true));

Not directly, but if the class has a constructor with parameter names that match the keys in the JSON object, you can simply decode the JSON into an associative array and pass it to the constructor via the '...' (argument unpacking) operator:

<?php
class MyClass {
    public function __construct(
        public int $id,
        public string $name,
        public array $attributes,
    ){}
}
$json = '{"name":"foo","id":42,"attributes":{"color":"red"}}';
$object = new MyClass(...json_decode($json, true));
print_r($object);

Output:

MyClass Object
(
    [id] => 42
    [name] => foo
    [attributes] => Array
        (
            [color] => red
        )

)

However, in practice, there is often some additional mapping to do, especially sub-objects that need to be recursively decoded too. So usually it is better to have a static fromArray function in each class that pre-processes the json-decoded array before passing the result to the constructor:

class Part {
    public function __construct(public float $weight){}
    public static function fromArray(array $data): self {
        return new self(...$data);
    }
}
class System {
    public function __construct(
        public string $name,
        public Part $mainPart,
        public array $otherParts,
    ){}
    public static function fromArray(array $data): self {
        $data['mainPart'] = Part::fromArray($data['mainPart']);
        $data['otherParts'] = array_map(Part::fromArray(...), $data['otherParts']); // php 8.1
        return new self(...$data);
    }
}
$json = '{"name":"foo","mainPart":{"weight":2},"otherParts":[{"weight":1}, {"weight":0.5}]}';
$object = System::fromArray(json_decode($json, true));
时常饿 2024-10-31 17:35:52

所有这些都激发了我对通用函数的灵感:

function loadJSON($Obj, $json)
{
     $dcod = json_decode($json);
     $prop = get_object_vars ( $dcod );
     foreach($prop as $key => $lock)
     {
        if(property_exists ( $Obj ,  $key ))
        {
            if(is_object($dcod->$key))
            {
                loadJSON($Obj->$key, json_encode($dcod->$key));
            }
            else
            {
                $Obj->$key = $dcod->$key;
            }
        }
    }
    return $Obj;
}

在类声明中调用:

class Bar{public $bar = " Boss";}
class Bas
{
    public $ber ;
    public $bas=" Boven"; 
    public function __construct() 
        {$this->ber = new Bar;}
}
class Baz
{
    public $bes ;
    public $baz=" Baaz";  
    public function __construct() 
        {$this->bes = new Bas;}
}

$Bazjson = '{"bes":{"ber":{"bar":"Baas"}}}';

$Bazobj = new Baz;

loadJSON($Bazobj, $Bazjson);
var_dump($Bazobj);

All this here inspired me to a generic function:

function loadJSON($Obj, $json)
{
     $dcod = json_decode($json);
     $prop = get_object_vars ( $dcod );
     foreach($prop as $key => $lock)
     {
        if(property_exists ( $Obj ,  $key ))
        {
            if(is_object($dcod->$key))
            {
                loadJSON($Obj->$key, json_encode($dcod->$key));
            }
            else
            {
                $Obj->$key = $dcod->$key;
            }
        }
    }
    return $Obj;
}

to be called in class declaration:

class Bar{public $bar = " Boss";}
class Bas
{
    public $ber ;
    public $bas=" Boven"; 
    public function __construct() 
        {$this->ber = new Bar;}
}
class Baz
{
    public $bes ;
    public $baz=" Baaz";  
    public function __construct() 
        {$this->bes = new Bas;}
}

$Bazjson = '{"bes":{"ber":{"bar":"Baas"}}}';

$Bazobj = new Baz;

loadJSON($Bazobj, $Bazjson);
var_dump($Bazobj);
烧了回忆取暖 2024-10-31 17:35:51

不是自动的。但你可以按照老式的路线来做。

$data = json_decode($json, true);

$class = new Whatever();
foreach ($data as $key => $value) $class->{$key} = $value;

或者,您可以使其更加自动化:

class Whatever {
    public function set($data) {
        foreach ($data AS $key => $value) $this->{$key} = $value;
    }
}

$class = new Whatever();
$class->set($data);

编辑:变得更奇特:

class JSONObject {
    public function __construct($json = false) {
        if ($json) $this->set(json_decode($json, true));
    }

    public function set($data) {
        foreach ($data AS $key => $value) {
            if (is_array($value)) {
                $sub = new JSONObject;
                $sub->set($value);
                $value = $sub;
            }
            $this->{$key} = $value;
        }
    }
}

// These next steps aren't necessary. I'm just prepping test data.
$data = array(
    "this" => "that",
    "what" => "who",
    "how" => "dy",
    "multi" => array(
        "more" => "stuff"
    )
);
$jsonString = json_encode($data);

// Here's the sweetness.
$class = new JSONObject($jsonString);
print_r($class);

Not automatically. But you can do it the old fashioned route.

$data = json_decode($json, true);

$class = new Whatever();
foreach ($data as $key => $value) $class->{$key} = $value;

Or alternatively, you could make that more automatic:

class Whatever {
    public function set($data) {
        foreach ($data AS $key => $value) $this->{$key} = $value;
    }
}

$class = new Whatever();
$class->set($data);

Edit: getting a little fancier:

class JSONObject {
    public function __construct($json = false) {
        if ($json) $this->set(json_decode($json, true));
    }

    public function set($data) {
        foreach ($data AS $key => $value) {
            if (is_array($value)) {
                $sub = new JSONObject;
                $sub->set($value);
                $value = $sub;
            }
            $this->{$key} = $value;
        }
    }
}

// These next steps aren't necessary. I'm just prepping test data.
$data = array(
    "this" => "that",
    "what" => "who",
    "how" => "dy",
    "multi" => array(
        "more" => "stuff"
    )
);
$jsonString = json_encode($data);

// Here's the sweetness.
$class = new JSONObject($jsonString);
print_r($class);
世态炎凉 2024-10-31 17:35:51

我们构建了 JsonMapper 来自动将 JSON 对象映射到我们自己的模型类上。它适用于嵌套/子对象。

它仅依赖于文档块类型信息进行映射,大多数类属性无论如何都有:

<?php
$mapper = new JsonMapper();
$contactObject = $mapper->map(
    json_decode(file_get_contents('http://example.org/contact.json')),
    new Contact()
);
?>

We built JsonMapper to map JSON objects onto our own model classes automatically. It works fine with nested/child objects.

It only relies on docblock type information for mapping, which most class properties have anyway:

<?php
$mapper = new JsonMapper();
$contactObject = $mapper->map(
    json_decode(file_get_contents('http://example.org/contact.json')),
    new Contact()
);
?>
鹊巢 2024-10-31 17:35:51

你可以做到——这是一个拼凑,但完全有可能。当我们开始将东西存储在沙发底座中时,我们必须这样做。

$stdobj = json_decode($json_encoded_myClassInstance);  //JSON to stdClass
$temp = serialize($stdobj);                   //stdClass to serialized

// Now we reach in and change the class of the serialized object
$temp = preg_replace('@^O:8:"stdClass":@','O:7:"MyClass":',$temp);

// Unserialize and walk away like nothing happend
$myClassInstance = unserialize($temp);   // Presto a php Class 

在我们的基准测试中,这比尝试迭代所有类变量要快得多。

警告:不适用于 stdClass 之外的嵌套对象

编辑:请记住数据源,强烈建议您不要在没有仔细分析风险的情况下使用来自用户的不受信任的数据执行此操作。

You can do it - it's a kludge but totally possible. We had to do when we started storing things in couchbase.

$stdobj = json_decode($json_encoded_myClassInstance);  //JSON to stdClass
$temp = serialize($stdobj);                   //stdClass to serialized

// Now we reach in and change the class of the serialized object
$temp = preg_replace('@^O:8:"stdClass":@','O:7:"MyClass":',$temp);

// Unserialize and walk away like nothing happend
$myClassInstance = unserialize($temp);   // Presto a php Class 

In our benchmarks this was way faster than trying to iterate through all the class variables.

Caveat: Won't work for nested objects other than stdClass

Edit: keep in mind the data source, it's strongly recommended that you don't do this withe untrusted data from users without a very carful analysis of the risks.

别闹i 2024-10-31 17:35:51

您可以使用 Johannes Schmitt 的序列化程序库

$serializer = JMS\Serializer\SerializerBuilder::create()->build();
$object = $serializer->deserialize($jsonData, 'MyNamespace\MyObject', 'json');

在最新版本的 JMS 序列化器中,语法为:

$serializer = SerializerBuilder::create()->build();
$object = $serializer->deserialize($jsonData, MyObject::class, 'json');

You could use Johannes Schmitt's Serializer library.

$serializer = JMS\Serializer\SerializerBuilder::create()->build();
$object = $serializer->deserialize($jsonData, 'MyNamespace\MyObject', 'json');

In the latest version of the JMS serializer the syntax is:

$serializer = SerializerBuilder::create()->build();
$object = $serializer->deserialize($jsonData, MyObject::class, 'json');
计㈡愣 2024-10-31 17:35:51

我很惊讶还没有人提到这一点。

使用 Symfony 序列化器组件: https://symfony.com/doc/current/components/serializer .html

从对象序列化为 JSON:

use App\Model\Person;

$person = new Person();
$person->setName('foo');
$person->setAge(99);
$person->setSportsperson(false);

$jsonContent = $serializer->serialize($person, 'json');

// $jsonContent contains {"name":"foo","age":99,"sportsperson":false,"createdAt":null}

echo $jsonContent; // or return it in a Response

从 JSON 反序列化为对象: (本示例使用 XML 只是为了演示格式的灵活性)

use App\Model\Person;

$data = <<<EOF
<person>
    <name>foo</name>
    <age>99</age>
    <sportsperson>false</sportsperson>
</person>
EOF;

$person = $serializer->deserialize($data, Person::class, 'xml');

I'm surprised no one mentioned this, yet.

Use the Symfony Serializer component: https://symfony.com/doc/current/components/serializer.html

Serializing from Object to JSON:

use App\Model\Person;

$person = new Person();
$person->setName('foo');
$person->setAge(99);
$person->setSportsperson(false);

$jsonContent = $serializer->serialize($person, 'json');

// $jsonContent contains {"name":"foo","age":99,"sportsperson":false,"createdAt":null}

echo $jsonContent; // or return it in a Response

Deserializing from JSON to Object: (this example uses XML just to demonstrate the flexibility of formats)

use App\Model\Person;

$data = <<<EOF
<person>
    <name>foo</name>
    <age>99</age>
    <sportsperson>false</sportsperson>
</person>
EOF;

$person = $serializer->deserialize($data, Person::class, 'xml');
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文