json_decode ke kelas khusus

90

Apakah mungkin untuk memecahkan kode string json ke objek selain stdClass?

Tom
sumber
4
Tidak ada yang baru dalam hal ini setelah bertahun-tahun?
Victor
Saya menemukan solusi yang berfungsi untuk saya stackoverflow.com/a/48838378/8138241
Jason Liu

Jawaban:

96

Tidak otomatis. Tapi Anda bisa melakukannya dengan cara lama.

$data = json_decode($json, true);

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

Atau sebagai alternatif, Anda dapat membuatnya lebih otomatis:

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

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

Sunting : menjadi sedikit lebih menarik:

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);
Michael McTiernan
sumber
1
Saya menyukai saran Anda, hanya untuk berkomentar bahwa itu tidak akan berfungsi dengan objek bersarang (selain STDClass atau objek yang dikonversi)
javier_domenech
36

Kami membangun JsonMapper untuk memetakan objek JSON ke kelas model kami sendiri secara otomatis. Ini berfungsi dengan baik dengan objek bersarang / anak.

Ini hanya bergantung pada informasi jenis docblock untuk pemetaan, yang kebanyakan properti kelas miliki:

<?php
$mapper = new JsonMapper();
$contactObject = $mapper->map(
    json_decode(file_get_contents('http://example.org/contact.json')),
    new Contact()
);
?>
cweiske.dll
sumber
1
WOW! Itu luar biasa.
vothaison
Bisakah Anda menjelaskan lisensi OSL3? Jika saya menggunakan JsonMapper di situs web, haruskah saya merilis kode sumber situs web itu? Jika saya menggunakan JsonMapper dalam kode di perangkat yang saya jual, apakah semua kode perangkat itu harus open source?
EricP
1
Tidak, Anda hanya perlu menerbitkan perubahan yang Anda lakukan pada JsonMapper itu sendiri.
cweiske
29

Anda bisa melakukannya - itu kludge tetapi sangat mungkin. Kami harus melakukannya ketika kami mulai menyimpan barang-barang di 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 

Dalam tolok ukur kami, ini jauh lebih cepat daripada mencoba mengulang melalui semua variabel kelas.

Peringatan: Tidak akan berfungsi untuk objek bertingkat selain stdClass

Edit: perhatikan sumber data, sangat disarankan agar Anda tidak melakukan ini dengan data tidak tepercaya dari pengguna tanpa analisis risiko yang cermat.

John Pettitt
sumber
1
Apakah ini berfungsi dengan subclass yang dienkapsulasi. Misalnya { "a": {"b":"c"} }, di mana objeknya aadalah kelas lain dan bukan hanya array asosiatif?
J-Rou
2
tidak, json_decode membuat objek stdclass, termasuk sub objek, jika Anda ingin memiliki objek lain, Anda harus meng-kludge setiap objek seperti di atas.
John Pettitt
Terima kasih, itulah yang saya bayangkan
J-Rou
Bagaimana kalau menggunakan solusi ini pada objek di mana konstruktor memiliki parameter. Saya tidak bisa membuatnya bekerja. Saya berharap seseorang dapat mengarahkan saya ke arah yang benar agar solusi ini berfungsi dengan objek yang memiliki konstruktor khusus dengan parameter.
Marco
Saya melanjutkan dan membangun ini menjadi sebuah fungsi. Perhatikan bahwa itu masih tidak berfungsi dengan subclass. gist.github.com/sixpeteunder/2bec86208775f131ce686d42f18d8621
Peter Lenjo
17

Anda dapat menggunakan pustaka Serializer J ohannes Schmitt .

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

Dalam versi terbaru serializer JMS, sintaksnya adalah:

$serializer = SerializerBuilder::create()->build();
$object = $serializer->deserialize($jsonData, MyObject::class, 'json');
Maleakhi
sumber
2
Sintaksnya tidak bergantung pada versi JMS Serializer, melainkan pada versi PHP - mulai dari PHP5.5 Anda dapat menggunakan ::classnotasi: php.net/manual/en/…
Ivan Yarych
4

Anda dapat membuat pembungkus untuk objek Anda dan membuat pembungkus terlihat seperti objek itu sendiri. Dan itu akan bekerja dengan objek bertingkat.

<?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
Yevgeniy Afanasyev
sumber
3

Tidak, ini tidak mungkin dilakukan mulai PHP 5.5.1.

Satu-satunya hal yang mungkin adalah memiliki json_decodearray asosiasi yang dikembalikan, bukan objek StdClass.

Gordon
sumber
3

Anda dapat melakukannya dengan cara di bawah ini ..

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

?>

Untuk detail selengkapnya, kunjungi create-custom-class-in-php-from-json-or-array

jigarshahindia
sumber
3

Saya terkejut belum ada yang menyebutkan ini.

Gunakan komponen Symfony Serializer: https://symfony.com/doc/current/components/serializer.html

Membuat serial dari Object ke 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

Deserialisasi dari JSON ke Objek: (contoh ini menggunakan XML hanya untuk mendemonstrasikan fleksibilitas format)

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');
Lucas Bustamante
sumber
2

Gunakan Refleksi :

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;
}
luke23489
sumber
1

Seperti yang dikatakan Gordon, itu tidak mungkin. Tetapi jika Anda mencari cara untuk mendapatkan string yang dapat didekode sebagai instance dari kelas give, Anda dapat menggunakan serialize dan unserialize sebagai gantinya.

class Foo
{

    protected $bar = 'Hello World';

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

}

$string = serialize(new Foo);

$foo = unserialize($string);
echo $foo->getBar();
Francesco Terenzani
sumber
Ini sepertinya tidak menjawab pertanyaan itu. Jika ya, Anda harus memberikan penjelasan.
Felix Kling
1

Saya pernah membuat kelas dasar abstrak untuk tujuan ini. Sebut saja JsonConvertible. Ini harus membuat serial dan deserialisasi anggota publik. Hal ini dimungkinkan dengan menggunakan Refleksi dan pengikatan statis akhir.

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

Hanya dari ingatan, jadi mungkin tidak sempurna. Anda juga harus mengecualikan properti statis dan mungkin memberi kelas turunan kesempatan untuk membuat beberapa properti diabaikan saat diserialkan ke / dari json. Meski begitu, saya harap Anda mengerti.

klawipo
sumber
0

JSON adalah protokol sederhana untuk mentransfer data antara berbagai bahasa pemrograman (dan itu juga merupakan bagian dari JavaScript) yang hanya mendukung jenis tertentu: angka, string, array / daftar, objek / dicts. Objek hanyalah peta key = value dan Array adalah daftar yang diurutkan.

Jadi tidak ada cara untuk mengekspresikan objek khusus dengan cara yang umum. Solusinya adalah dengan menentukan struktur di mana program Anda akan tahu bahwa itu adalah objek khusus.

Berikut contohnya:

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

Ini dapat digunakan untuk membuat instance dari MyClassdan mengatur bidang adan fooke 123dan "bar".

ThiefMaster
sumber
6
Ini mungkin benar, tetapi pertanyaannya bukanlah menanyakan tentang merepresentasikan objek secara umum. Sepertinya dia punya tas JSON khusus yang memetakan ke kelas tertentu di salah satu atau kedua ujungnya. Tidak ada alasan Anda tidak dapat menggunakan JSON sebagai serialisasi eksplisit dari kelas bernama non-generik dengan cara ini. Menamainya seperti yang Anda lakukan boleh saja jika Anda menginginkan solusi umum, tetapi tidak ada salahnya juga jika Anda memiliki kontrak yang disepakati pada struktur JSON.
DougW
Ini bisa bekerja jika Anda mengimplementasikan Serializable di akhir encoding, dan memiliki persyaratan di akhir decoding. Bahkan bisa bekerja dengan subclass jika diatur dengan benar.
Peter Lenjo
0

Saya melanjutkan dan menerapkan jawaban John Petit , sebagai fungsi ( inti ):

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

Ini bekerja dengan sempurna untuk kasus penggunaan saya. Namun tanggapan Yevgeniy Afanasyev tampaknya sama menjanjikannya bagi saya. Mungkin saja kelas Anda memiliki "konstruktor" tambahan, seperti:

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

Ini juga terinspirasi oleh jawaban ini .

EDIT: Saya telah menggunakan karriereat / json-decoder untuk beberapa waktu sekarang, dan saya sama sekali tidak mengalami masalah dengannya. Ringan dan sangat mudah dikembangkan. Berikut adalah contoh pengikatan yang saya tulis untuk deserialisasi JSON menjadi objek Carbon / CarbonImmutable.

Peter Lenjo
sumber