【问题标题】:json_decode to custom classjson_decode 到自定义类
【发布时间】:2011-07-20 20:16:14
【问题描述】:

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

【问题讨论】:

标签: php json


【解决方案1】:

不,这在 PHP 5.5.1 中是不可能的。

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

【讨论】:

    【解决方案2】:

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

    class Foo
    {
    
        protected $bar = 'Hello World';
    
        function getBar() {
            return $this->bar;
        }
    
    }
    
    $string = serialize(new Foo);
    
    $foo = unserialize($string);
    echo $foo->getBar();
    

    【讨论】:

    • 这似乎没有解决问题。如果是这样,您必须提供一些解释。
    【解决方案3】:

    不是自动的。但是你可以用老式的方法来做。

    $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);
    

    【讨论】:

    • 我喜欢你的建议,只是说明它不适用于嵌套对象(STDClass 或转换后的对象除外)
    【解决方案4】:

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

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

    这是一个例子:

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

    这可用于创建MyClass 的实例并将字段afoo 设置为123"bar"

    【讨论】:

    • 这可能是真的,但问题不是要以通用方式表示对象。听起来他有一个特定的 JSON 包,可以映射到一端或两端的特定类。没有理由不能以这种方式将 JSON 用作非泛型命名类的显式序列化。如果您想要一个通用的解决方案,按照您正在做的方式命名它是可以的,但是在 JSON 结构上达成一致的合同也没有错。
    • 如果您在编码端实现 Serializable 并在解码端有条件,这可能会起作用。如果组织得当,甚至可以使用子类。
    【解决方案5】:

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

    $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 以外的嵌套对象

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

    【讨论】:

    • 这是否适用于封装的子类。例如。 { "a": {"b":"c"} },其中a 中的对象属于另一个类,而不仅仅是一个关联数组?
    • 不,json_decode 创建 stdclass 对象,包括子对象,如果你想让它们成为其他任何东西,你必须像上面那样组合每个对象。
    • 谢谢,这就是我的想象
    • 在构造函数有参数的对象上使用这个解决方案怎么样。我无法让它工作。我希望有人能指出我正确的方向,以使该解决方案与具有自定义构造函数的对象一起使用。
    • 我继续并将其构建到一个函数中。请注意,它仍然不适用于子类。 gist.github.com/sixpeteunder/2bec86208775f131ce686d42f18d8621
    【解决方案6】:

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

    它只依赖于映射的 docblock 类型信息,大多数类属性无论如何都有:

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

    【讨论】:

    • 哇!这真是太棒了。
    • 您能解释一下 OSL3 许可证吗?如果我在网站上使用 JsonMapper,我必须发布该网站的源代码吗?如果我在我销售的设备上的代码中使用 JsonMapper,那么该设备的所有代码都必须是开源的吗?
    • 不,您只需将所做的更改发布到 JsonMapper 本身。
    【解决方案7】:

    您可以使用 Johannes Schmitt's Serializer library

    $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');
    

    【讨论】:

    • 语法不依赖于JMS Serializer版本,而是依赖于PHP版本——从PHP5.5开始你可以使用::class符号:php.net/manual/en/…
    【解决方案8】:

    我曾经为此创建了一个抽象基类。我们称之为 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 时忽略某些属性。不过,我希望你能理解。

    【讨论】:

      【解决方案9】:

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

      <?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-or-array

      【讨论】:

        【解决方案10】:

        您可以为您的对象制作一个包装器,并使包装器看起来像是对象本身。它适用于多级对象。

        <?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
        

        【讨论】:

          【解决方案11】:

          我继续实现John Petit's answer,作为一个函数(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's response 对我来说似乎同样有希望。可以让您的班级有一个额外的“构造函数”,如下所示:

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

          这也是受到this answer的启发。

          编辑:我使用karriereat/json-decoder 已经有一段时间了,我完全没有遇到任何问题。它是轻量级的并且非常容易扩展。 Here's an example 我为将 JSON 反序列化为 Carbon/CarbonImmutable 对象而编写的绑定。

          【讨论】:

            【解决方案12】:

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

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

            从 Object 序列化为 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 反序列化为 Object:(本示例使用 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');
            

            【讨论】:

              【解决方案13】:

              使用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;
              }
              

              【讨论】:

                【解决方案14】:

                这里的所有这些都启发了我一个通用函数:

                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;
                            }
                        }
                    }
                }
                

                在类声明中调用:

                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);
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-01-25
                  • 2018-01-25
                  • 2014-08-21
                  • 2011-07-28
                  • 2020-09-09
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多