json_decode alla classe personalizzata


Risposte:


96

Non automaticamente. Ma puoi farlo alla vecchia maniera.

$data = json_decode($json, true);

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

O in alternativa, potresti renderlo più automatico:

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

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

Modifica : diventare un po 'più elaborato:

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
Mi piacciono i tuoi suggerimenti, solo per sottolineare che non funzionerà con oggetti nidificati (diversi da STDClass o l'oggetto convertito)
javier_domenech

34

Abbiamo costruito JsonMapper per mappare automaticamente gli oggetti JSON sulle nostre classi modello. Funziona bene con oggetti nidificati / figli.

Si basa solo sulle informazioni sul tipo di docblock per la mappatura, che la maggior parte delle proprietà di classe hanno comunque:

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

1
WOW! È semplicemente fantastico.
vothaison

Puoi spiegare la licenza OSL3? Se utilizzo JsonMapper su un sito Web, devo rilasciare il codice sorgente di quel sito Web? Se utilizzo JsonMapper nel codice su un dispositivo che vendo, tutto il codice di quel dispositivo deve essere open source?
EricP

No, devi solo pubblicare le modifiche apportate a JsonMapper stesso.
cweiske il

29

Puoi farlo - è un kludge ma totalmente possibile. Dovevamo farlo quando abbiamo iniziato a memorizzare le cose 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 

Nei nostri benchmark questo è stato molto più veloce rispetto al tentativo di iterare tutte le variabili di classe.

Avvertenza: non funzionerà per oggetti nidificati diversi da stdClass

Modifica: tieni presente l'origine dei dati, si consiglia vivamente di non farlo con dati non attendibili degli utenti senza un'analisi molto attenta dei rischi.


1
Funziona con le sottoclassi incapsulate. Ad esempio { "a": {"b":"c"} }, dove l'oggetto aè di un'altra classe e non solo un array associativo?
J-Rou

2
no, json_decode crea oggetti stdclass, inclusi suboggetti, se vuoi che siano qualcos'altro devi eliminare ogni oggetto come sopra.
John Pettitt

Grazie, è quello che immaginavo
J-Rou

Che ne dici di usare questa soluzione su oggetti in cui il costruttore ha parametri. Non riesco a farlo funzionare. Speravo che qualcuno potesse indicarmi la giusta direzione per far funzionare questa soluzione con un oggetto che ha un costruttore personalizzato con parametri.
Marco

Sono andato avanti e l'ho integrato in una funzione. Nota che ancora non funziona con le sottoclassi. gist.github.com/sixpeteunder/2bec86208775f131ce686d42f18d8621
Peter Lenjo

16

Potresti usare la libreria Serializer di J ohannes Schmitt .

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

Nell'ultima versione del serializzatore JMS la sintassi è:

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

2
La sintassi non dipende versione JMS Serializer, ma piuttosto sulla versione di PHP - a partire da PHP5.5 è possibile utilizzare ::classla notazione: php.net/manual/en/...
Ivan Yarych

4

Puoi creare un involucro per il tuo oggetto e fare in modo che l'involucro sembri l'oggetto stesso. E funzionerà con oggetti multilivello.

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

No, questo non è possibile a partire da PHP 5.5.1.

L'unica cosa possibile è avere json_decodematrici associate di ritorno invece degli oggetti StdClass.


3

Puoi farlo nel modo seguente ..

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

?>

Per maggiori dettagli visita create-custom-class-in-php-from-json-or-array


3

Sono sorpreso che nessuno ne abbia parlato ancora.

Usa il componente Serializer di Symfony: https://symfony.com/doc/current/components/serializer.html

Serializzazione da Object a 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

Deserializzazione da JSON a Object: (questo esempio utilizza XML solo per dimostrare la flessibilità dei formati)

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

Usa la riflessione :

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

Come dice Gordon non è possibile. Ma se stai cercando un modo per ottenere una stringa che possa essere decodificata come istanza di una classe give, puoi invece usare serialize e unserialize.

class Foo
{

    protected $bar = 'Hello World';

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

}

$string = serialize(new Foo);

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

Questo non sembra rispondere alla domanda. In caso affermativo, devi fornire alcune spiegazioni.
Felix Kling

1

Una volta ho creato una classe base astratta per questo scopo. Chiamiamolo JsonConvertible. Dovrebbe serializzare e deserializzare i membri pubblici. Ciò è possibile utilizzando la riflessione e l'associazione statica tardiva.

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

Solo dalla memoria, quindi probabilmente non impeccabile. Dovrai anche escludere proprietà statiche e potresti dare alle classi derivate la possibilità di ignorare alcune proprietà quando serializzate da / a json. Spero che tu abbia comunque l'idea.


0

JSON è un semplice protocollo per trasferire dati tra vari linguaggi di programmazione (ed è anche un sottoinsieme di JavaScript) che supporta solo alcuni tipi: numeri, stringhe, array / elenchi, oggetti / dict. Gli oggetti sono solo key = value map e gli array sono elenchi ordinati.

Quindi non c'è modo di esprimere oggetti personalizzati in modo generico. La soluzione è definire una struttura in cui i tuoi programmi sapranno che si tratta di un oggetto personalizzato.

Ecco un esempio:

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

Questo potrebbe essere utilizzato per creare un'istanza di MyClasse impostare i campi ae foosu 123e "bar".


6
Questo può essere vero, ma la domanda non si pone sulla rappresentazione degli oggetti in modo generico. Sembra che abbia una borsa JSON specifica che mappa a una classe specifica su una o entrambe le estremità. Non c'è motivo per cui non puoi usare JSON come serializzazione esplicita di classi denominate non generiche in questo modo. Denominarlo come stai facendo va bene se vuoi una soluzione generica, ma non c'è niente di sbagliato nell'avere un contratto concordato sulla struttura JSON.
DougW

Questo potrebbe funzionare se implementi Serializable alla fine della codifica e hai condizionali alla fine della decodifica. Potrebbe anche funzionare con le sottoclassi se organizzate correttamente.
Peter Lenjo

0

Sono andato avanti e ho implementato la risposta di John Petit , in funzione ( sintesi ):

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

Ha funzionato perfettamente per il mio caso d'uso. Tuttavia la risposta di Yevgeniy Afanasyev mi sembra altrettanto promettente. Potrebbe essere possibile che la tua classe abbia un "costruttore" extra, in questo modo:

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

Anche questo è ispirato da questa risposta .


-1

Penso che il modo più semplice sia:

function mapJSON($json, $class){
$decoded_object = json_decode($json);
   foreach ($decoded_object as $key => $value) {
            $class->$key = $value;
   }
   return $class;}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.