json_decode özel sınıfa


Yanıtlar:


96

Otomatik olarak değil. Ama bunu eski moda yoldan yapabilirsiniz.

$data = json_decode($json, true);

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

Veya alternatif olarak, bunu daha otomatik hale getirebilirsiniz:

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

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

Düzenleme : Biraz meraklı olmak:

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
Önerilerinizi beğendim, sadece iç içe geçmiş nesnelerle (STDClass veya dönüştürülmüş nesne dışında)
çalışmayacağını belirtmek için

36

Biz inşa JsonMapper otomatik kendi modeli sınıfları üzerine JSON işlevleri gerçekleştirilmektedir. İç içe / alt nesnelerle iyi çalışır.

Yalnızca eşleme için docblock türü bilgilerine dayanır; bu, çoğu sınıf özelliğinde zaten vardır:

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

1
VAOV! Bu harika.
vothaison

OSL3 lisansını açıklar mısınız? JsonMapper'ı bir web sitesinde kullanırsam, o web sitesinin kaynak kodunu yayınlamalı mıyım? JsonMapper'ı sattığım bir cihazda kod olarak kullanırsam, bu cihazın tüm kodunun açık kaynak olması gerekir mi?
EricP

1
Hayır, yalnızca yaptığınız değişiklikleri JsonMapper'ın kendisine yayınlamanız gerekir.
cweiske

29

Yapabilirsiniz - bu bir kludge ama tamamen mümkün. Bir şeyleri kanepe tabanında depolamaya başladığımızda yapmak zorundaydık.

$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 

Kıyaslamalarımızda bu, tüm sınıf değişkenlerini yinelemeye çalışmaktan çok daha hızlıydı.

Uyarı: stdClass dışındaki iç içe nesneler için çalışmaz

Düzenleme: Veri kaynağını aklınızda bulundurun, bunu risklerin çok dikkatli bir analizi yapılmadan kullanıcılardan gelen güvenilmeyen verilerle yapmamanız şiddetle tavsiye edilir.


1
Bu, kapsüllenmiş alt sınıflarla çalışır mı? Örneğin { "a": {"b":"c"} }, nesnenin abaşka bir sınıfta olduğu ve yalnızca ilişkilendirilebilir bir dizi olmadığı durumda?
J-Rou

2
hayır, json_decode alt nesneler de dahil olmak üzere stdclass nesneler oluşturur, eğer bunların başka bir şey olmasını istiyorsanız, yukarıdaki gibi her nesneyi kludge etmeniz gerekir.
John Pettitt

Teşekkür ederim, hayal ettiğim buydu
J-Rou

Yapıcının parametrelere sahip olduğu nesnelerde bu çözümü kullanmaya ne dersiniz? Çalışmasını sağlayamıyorum. Bu çözümün parametrelere sahip özel bir kurucuya sahip bir nesneyle çalışması için birisinin beni doğru yöne yönlendirebileceğini umuyordum.
Marco

Devam ettim ve bunu bir fonksiyona dönüştürdüm. Alt sınıflarda hala çalışmadığını unutmayın. gist.github.com/sixpeteunder/2bec86208775f131ce686d42f18d8621
Peter Lenjo

17

J ohannes Schmitt'in Serializer kitaplığını kullanabilirsiniz .

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

JMS serileştiricinin en son sürümünde sözdizimi şu şekildedir:

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

2
Sözdizimi JMS Serializer sürümüne değil, PHP sürümüne bağlıdır - PHP5.5'ten başlayarak ::classgösterimi kullanabilirsiniz : php.net/manual/en/…
Ivan Yarych

4

Nesneniz için bir sarmalayıcı yapabilir ve sarmalayıcının nesnenin kendisi gibi görünmesini sağlayabilirsiniz. Ve çok düzeyli nesnelerle çalışacaktır.

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

Hayır, bu PHP 5.5.1'den itibaren mümkün değildir.

Mümkün olan tek şey json_decode, StdClass nesneleri yerine döndürülen ilişkili dizilere sahip olmaktır.



3

Henüz kimsenin bundan bahsetmediğine şaşırdım.

Symfony Serializer bileşenini kullanın: https://symfony.com/doc/current/components/serializer.html

Nesneden JSON'a seri hale getirme:

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'dan Nesneye seriyi kaldırma: (bu örnek, yalnızca formatların esnekliğini göstermek için XML kullanır)

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

Yansımayı Kullan :

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

Gordon'un dediği gibi mümkün değil. Ancak, bir ver sınıfının örneği olarak kodu çözülebilecek bir dizge elde etmenin bir yolunu arıyorsanız, bunun yerine serileştirme ve serileştirmeyi kullanabilirsiniz .

class Foo
{

    protected $bar = 'Hello World';

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

}

$string = serialize(new Foo);

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

Bu soruyu yanıtlamıyor gibi görünüyor. Varsa, bazı açıklamalar sağlamalısınız.
Felix Kling

1

Bir keresinde bu amaçla soyut bir temel sınıf yaratmıştım. Buna JsonConvertible diyelim. Kamu üyelerini seri hale getirmeli ve serisini kaldırmalıdır. Bu, Yansıma ve geç statik bağlama kullanılarak mümkündür.

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

Sadece hafızadan, bu yüzden muhtemelen kusursuz değil. Ayrıca, statik özellikleri hariç tutmanız gerekecek ve türetilmiş sınıflara, json'a / json'dan serileştirildiğinde bazı özellikleri göz ardı etme şansı verebilirsiniz. Umarım yine de anlarsın.


0

JSON, yalnızca belirli türleri destekleyen çeşitli programlama dilleri (ve aynı zamanda bir JavaScript alt kümesidir) arasında veri aktarımı için basit bir protokoldür: sayılar, dizeler, diziler / listeler, nesneler / dikteler. Nesneler sadece anahtar = değer eşlemleridir ve Diziler sıralı listelerdir.

Bu nedenle, özel nesneleri genel bir şekilde ifade etmenin bir yolu yoktur. Çözüm, programlarınızın özel bir nesne olduğunu bilecekleri bir yapı tanımlamaktır.

İşte bir örnek:

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

Bu bir örneğini oluşturmak için kullanılabilecek MyClassve alanları ayarlamak ave fookarşı 123ve "bar".


6
Bu doğru olabilir, ancak soru nesneleri genel bir şekilde temsil etmeyi sormuyor. Görünüşe göre bir veya her iki ucunda belirli bir sınıfa eşlenen belirli bir JSON çantası var. JSON'u bu şekilde genel olmayan adlandırılmış sınıfların açık bir serileştirmesi olarak kullanmamanız için hiçbir neden yok. Genel bir çözüm istiyorsanız, bunu yaptığınız gibi adlandırmak iyidir, ancak JSON yapısı üzerinde üzerinde anlaşmaya varılmış bir sözleşmeye sahip olmakta da yanlış bir şey yoktur.
DougW

Bu, kodlama ucunda Serializable uygularsanız ve kod çözme ucunda koşullara sahipseniz işe yarayabilir. Düzgün organize edilirse alt sınıflarla bile çalışabilir.
Peter Lenjo

0

Devam ettim ve John Petit'in cevabını bir işlev ( özet ) olarak uyguladım :

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

Bu benim kullanım durumum için mükemmel çalıştı. Ancak Yevgeniy Afanasyev'in yanıtı da bana aynı derecede umut verici görünüyor. Sınıfınızın ekstra bir "kurucu" sahibi olması mümkün olabilir, örneğin:

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

Bu aynı zamanda bu cevaptan da esinlenmiştir .

DÜZENLEME: Bir süredir karriereat / json-decoder kullanıyorum ve kesinlikle hiçbir sorun yaşamadım. Hafiftir ve çok kolay genişletilebilir. JSON'u bir Carbon / CarbonImmutable nesnesine seri durumdan çıkarmak için yazdığım bir bağlama örneği .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.