json_decode cho lớp tùy chỉnh


Câu trả lời:


96

Không tự động. Nhưng bạn có thể làm theo cách cũ.

$data = json_decode($json, true);

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

Hoặc cách khác, bạn có thể làm cho điều đó tự động hơn:

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

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

Chỉnh sửa : nhận được một chút huyền ảo:

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

1
Tôi như bạn gợi ý, chỉ để nhận xét rằng nó sẽ không làm việc với các đối tượng lồng nhau (trừ stdClass hoặc đối tượng được chuyển đổi)
javier_domenech

34

Chúng tôi đã xây dựng JsonMapper để tự động ánh xạ các đối tượng JSON vào các lớp mô hình của riêng chúng tôi. Nó hoạt động tốt với các đối tượng lồng nhau / con.

Nó chỉ dựa vào thông tin loại docblock để ánh xạ, mà hầu hết các thuộc tính của lớp đều có:

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

1
WOW! Điều đó thật tuyệt vời.
vothaison

Bạn có thể giải thích giấy phép OSL3 không? Nếu tôi sử dụng JsonMapper trên một trang web, tôi có phải phát hành mã nguồn của trang web đó không? Nếu tôi sử dụng JsonMapper trong mã trên thiết bị mà tôi bán, thì tất cả mã của thiết bị đó có phải là mã nguồn mở không?
EricP

Không, bạn chỉ phải xuất bản những thay đổi bạn thực hiện với chính JsonMapper.
cweiske

29

Bạn có thể làm được - đó là một điều khó nhưng hoàn toàn có thể. Chúng tôi phải làm khi bắt đầu lưu trữ mọi thứ trong 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 

Trong các điểm chuẩn của chúng tôi, cách này nhanh hơn so với việc cố gắng lặp qua tất cả các biến lớp.

Lưu ý: Sẽ không hoạt động đối với các đối tượng lồng nhau ngoài stdClass

Chỉnh sửa: hãy ghi nhớ nguồn dữ liệu, bạn không nên làm điều này với dữ liệu không đáng tin cậy từ người dùng mà không có phân tích cẩn thận về rủi ro.


1
Điều này có hoạt động với các lớp con được đóng gói không. Ví dụ: { "a": {"b":"c"} }trong đó đối tượng thuộc amột lớp khác chứ không chỉ là một mảng kết hợp?
J-Rou

2
không, json_decode tạo các đối tượng stdclass, bao gồm cả các đối tượng con, nếu bạn muốn có chúng là bất kỳ thứ gì khác, bạn phải cắt từng đối tượng như trên.
John Pettitt

Cảm ơn bạn, đó là những gì tôi tưởng tượng
J-Rou

Làm thế nào về việc sử dụng giải pháp này trên các đối tượng mà hàm tạo có các tham số. Tôi không thể có được nó để làm việc. Tôi đã hy vọng ai đó có thể chỉ cho tôi đúng hướng để làm cho giải pháp này hoạt động với một đối tượng có hàm tạo tùy chỉnh với các tham số.
Marco

Tôi đã đi trước và xây dựng nó thành một hàm. Lưu ý rằng nó vẫn không hoạt động với các lớp con. gist.github.com/sixpeteunder/2bec86208775f131ce686d42f18d8621
Peter Lenjo

16

Bạn có thể sử dụng thư viện Serializer của J ohannes Schmitt .

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

Trong phiên bản mới nhất của bộ tuần tự JMS, cú pháp là:

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

2
Cú pháp là không phụ thuộc vào phiên bản JMS Serializer, nhưng thay vào phiên bản PHP - bắt đầu từ PHP5.5 bạn có thể sử dụng ::classký hiệu: php.net/manual/en/...
Ivan Yarych

4

Bạn có thể tạo một trình bao bọc cho đối tượng của mình và làm cho trình bao bọc trông giống như chính nó là đối tượng. Và nó sẽ hoạt động với các đối tượng đa cấp.

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

3

Không, điều này là không thể với PHP 5.5.1.

Điều duy nhất có thể là có json_decodecác mảng liên kết trả về thay vì các đối tượng StdClass.


3

Bạn có thể thực hiện theo cách dưới đây ..

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

?>

Để biết thêm chi tiết, hãy truy cập create-custom-class-in-php-from-json-or-array


3

Tôi ngạc nhiên là chưa có ai đề cập đến điều này.

Sử dụng thành phần Symfony Serializer: https://symfony.com/doc/current/components/serializer.html

Sắp xếp thứ tự từ Object sang 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

Hủy số liệu hóa từ JSON sang Đối tượng: (ví dụ này sử dụng XML chỉ để chứng minh tính linh hoạt của các định dạng)

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

2

Sử dụng phản chiếu :

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

1

Như Gordon nói là không thể. Nhưng nếu bạn đang tìm cách lấy một chuỗi có thể được giải mã dưới dạng một thể hiện của lớp give, bạn có thể sử dụng serialize và unserialize để thay thế.

class Foo
{

    protected $bar = 'Hello World';

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

}

$string = serialize(new Foo);

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

Điều này dường như không giải quyết được câu hỏi. Nếu có, bạn phải cung cấp một số giải thích.
Felix Kling

1

Tôi đã từng tạo một lớp cơ sở trừu tượng cho mục đích này. Hãy gọi nó là JsonConvertible. Nó sẽ tuần tự hóa và giải mã hóa các thành viên công cộng. Điều này có thể thực hiện được bằng cách sử dụng Phản chiếu và liên kết tĩnh muộn.

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

Chỉ từ trí nhớ, vì vậy có lẽ không hoàn hảo. Bạn cũng sẽ phải loại trừ các thuộc tính tĩnh và có thể cho các lớp dẫn xuất cơ hội để làm cho một số thuộc tính bị bỏ qua khi tuần tự hóa đến / từ json. Tôi hy vọng bạn có được ý tưởng, dù sao.


0

JSON là một giao thức đơn giản để truyền dữ liệu giữa các ngôn ngữ lập trình khác nhau (và nó cũng là một tập hợp con của JavaScript) chỉ hỗ trợ một số kiểu nhất định: số, chuỗi, mảng / danh sách, đối tượng / dicts. Đối tượng chỉ là bản đồ key = value và Mảng là danh sách có thứ tự.

Vì vậy, không có cách nào để thể hiện các đối tượng tùy chỉnh một cách chung chung. Giải pháp là xác định một cấu trúc mà (các) chương trình của bạn sẽ biết rằng đó là một đối tượng tùy chỉnh.

Đây là một ví dụ:

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

Điều này có thể được sử dụng để tạo một phiên bản của MyClassvà đặt các trường afoođến 123"bar".


6
Điều này có thể đúng, nhưng câu hỏi không hỏi về việc biểu diễn các đối tượng một cách chung chung. Có vẻ như anh ấy có một túi JSON cụ thể ánh xạ đến một lớp cụ thể ở một hoặc cả hai đầu. Không có lý do gì bạn không thể sử dụng JSON làm tuần tự hóa rõ ràng các lớp có tên không chung chung theo cách này. Đặt tên cho nó như bạn đang làm là tốt nếu bạn muốn một giải pháp chung, nhưng cũng không có gì sai khi có một hợp đồng đã thỏa thuận về cấu trúc JSON.
DougW

Điều này có thể hoạt động nếu bạn triển khai Có thể hóa nối tiếp ở đầu mã hóa và có các điều kiện ở đầu giải mã. Thậm chí có thể hoạt động với các lớp con nếu được tổ chức hợp lý.
Peter Lenjo

0

Tôi đã tiếp tục và triển khai câu trả lời của John Petit , dưới dạng một hàm ( ý chính ):

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

Điều này hoạt động hoàn hảo cho trường hợp sử dụng của tôi. Tuy nhiên , câu trả lời của Yevgeniy Afanasyev có vẻ hứa hẹn không kém đối với tôi. Có thể có lớp của bạn có thêm một "hàm tạo", như sau:

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

Đây cũng là cảm hứng từ câu trả lời này .


-1

Tôi nghĩ cách đơn giản nhất là:

function mapJSON($json, $class){
$decoded_object = json_decode($json);
   foreach ($decoded_object as $key => $value) {
            $class->$key = $value;
   }
   return $class;}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.