J'aime vos suggestions, juste pour remarquer que cela ne fonctionnera pas avec les objets imbriqués (autres que STDClass ou l'objet converti)
javier_domenech
34
Nous avons créé JsonMapper pour mapper automatiquement les objets JSON sur nos propres classes de modèle. Cela fonctionne très bien avec les objets imbriqués / enfants.
Il ne repose que sur les informations de type docblock pour le mappage, que la plupart des propriétés de classe ont de toute façon:
<?php
$mapper = new JsonMapper();
$contactObject = $mapper->map(
json_decode(file_get_contents('http://example.org/contact.json')),
new Contact()
);
?>
Pouvez-vous expliquer la licence OSL3? Si j'utilise JsonMapper sur un site Web, dois-je publier le code source de ce site Web? Si j'utilise JsonMapper dans le code sur un appareil que je vends, tout le code de cet appareil doit-il être open source?
EricP
Non, il vous suffit de publier les modifications que vous apportez à JsonMapper lui-même.
cweiske le
29
Vous pouvez le faire - c'est un kludge mais tout à fait possible. Nous devions faire quand nous avons commencé à ranger des choses dans un canapé.
$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
Dans nos benchmarks, c'était bien plus rapide que d'essayer d'itérer toutes les variables de classe.
Avertissement: ne fonctionnera pas pour les objets imbriqués autres que stdClass
Edit: gardez à l'esprit la source de données, il est fortement recommandé de ne pas le faire avec les données non fiables des utilisateurs sans une analyse très attentive des risques.
Cela fonctionne-t-il avec des sous-classes encapsulées? Par exemple { "a": {"b":"c"} }, où l'objet dans aest d'une autre classe et pas seulement d'un tableau associatif?
J-Rou
2
non, json_decode crée des objets stdclass, y compris des sous-objets, si vous voulez qu'ils soient n'importe quoi d'autre, vous devez supprimer chaque objet comme ci-dessus.
John Pettitt
Merci, c'est ce que j'ai imaginé
J-Rou
Que diriez-vous d'utiliser cette solution sur des objets où le constructeur a des paramètres. Je n'arrive pas à le faire marcher. J'espérais que quelqu'un pourrait me diriger dans la bonne direction pour faire fonctionner cette solution avec un objet qui a un constructeur personnalisé avec des paramètres.
La syntaxe ne dépend pas de la version de JMS Serializer, mais plutôt de la version PHP - à partir de PHP5.5, vous pouvez utiliser la ::classnotation: php.net/manual/en
Ivan Yarych
4
Vous pouvez créer un wrapper pour votre objet et donner l'impression que le wrapper est l'objet lui-même. Et cela fonctionnera avec des objets à plusieurs niveaux.
<?phpclassObj{
public $slave;
publicfunction__get($key) {
return property_exists ( $this->slave , $key ) ? $this->slave->{$key} : null;
}
publicfunction__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
<?phpclassCatalogProduct{
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;
}
}
}
}
useApp\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
Désérialisation de JSON à Object: (cet exemple utilise XML juste pour démontrer la flexibilité des formats)
Comme le dit Gordon, ce n'est pas possible. Mais si vous cherchez un moyen d'obtenir une chaîne qui peut être décodée comme une instance d'une classe donnée, vous pouvez utiliser à la place sérialiser et désérialiser.
Cela ne semble pas répondre à la question. Si c'est le cas, vous devez fournir des explications.
Felix Kling
1
J'ai créé une fois une classe de base abstraite à cet effet. Appelons cela JsonConvertible. Il doit sérialiser et désérialiser les membres publics. Ceci est possible en utilisant Reflection et la liaison statique tardive.
abstractclassJsonConvertible{
staticfunctionfromJson($json) {
$result = newstatic();
$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;
}
functiontoJson() {
return json_encode($this);
}
}
classMyClassextendsJsonConvertible{
public $name;
public $whatever;
}
$mine = MyClass::fromJson('{"name": "My Name", "whatever": "Whatever"}');
echo $mine->toJson();
Juste de mémoire, donc probablement pas parfait. Vous devrez également exclure les propriétés statiques et donner aux classes dérivées la possibilité de rendre certaines propriétés ignorées lorsqu'elles sont sérialisées vers / depuis json. J'espère que vous avez néanmoins l'idée.
JSON est un protocole simple pour transférer des données entre différents langages de programmation (et c'est aussi un sous-ensemble de JavaScript) qui ne prend en charge que certains types: nombres, chaînes, tableaux / listes, objets / dictionnaires. Les objets ne sont que des cartes clé = valeur et les tableaux sont des listes ordonnées.
Il n'y a donc aucun moyen d'exprimer des objets personnalisés de manière générique. La solution consiste à définir une structure dans laquelle vos programmes sauront qu'il s'agit d'un objet personnalisé.
Cela peut être vrai, mais la question ne concerne pas la représentation d'objets de manière générique. On dirait qu'il a un sac JSON spécifique qui correspond à une classe spécifique à une ou aux deux extrémités. Il n'y a aucune raison pour que vous ne puissiez pas utiliser JSON comme sérialisation explicite de classes nommées non génériques de cette manière. Le nommer comme vous le faites est bien si vous voulez une solution générique, mais il n'y a rien de mal à avoir un contrat convenu sur la structure JSON.
DougW
Cela pourrait fonctionner si vous implémentez Serializable à la fin du codage et que vous ayez des conditions à la fin du décodage. Peut même fonctionner avec des sous-classes si elles sont organisées correctement.
Cela a parfaitement fonctionné pour mon cas d'utilisation. Cependant, la réponse de Yevgeniy Afanasyev me semble tout aussi prometteuse. Il est possible que votre classe ait un "constructeur" supplémentaire, comme ceci:
publicstaticfunctionwithJson(string $json) {
$instance = newstatic();
// Do your thingreturn $instance;
}
Réponses:
Pas automatiquement. Mais vous pouvez le faire à l'ancienne.
$data = json_decode($json, true); $class = new Whatever(); foreach ($data as $key => $value) $class->{$key} = $value;
Ou bien, vous pouvez rendre cela plus automatique:
class Whatever { public function set($data) { foreach ($data AS $key => $value) $this->{$key} = $value; } } $class = new Whatever(); $class->set($data);
Edit : devenir un peu plus chic:
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);
la source
Nous avons créé JsonMapper pour mapper automatiquement les objets JSON sur nos propres classes de modèle. Cela fonctionne très bien avec les objets imbriqués / enfants.
Il ne repose que sur les informations de type docblock pour le mappage, que la plupart des propriétés de classe ont de toute façon:
<?php $mapper = new JsonMapper(); $contactObject = $mapper->map( json_decode(file_get_contents('http://example.org/contact.json')), new Contact() ); ?>
la source
Vous pouvez le faire - c'est un kludge mais tout à fait possible. Nous devions faire quand nous avons commencé à ranger des choses dans un canapé.
$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
Dans nos benchmarks, c'était bien plus rapide que d'essayer d'itérer toutes les variables de classe.
Avertissement: ne fonctionnera pas pour les objets imbriqués autres que stdClass
Edit: gardez à l'esprit la source de données, il est fortement recommandé de ne pas le faire avec les données non fiables des utilisateurs sans une analyse très attentive des risques.
la source
{ "a": {"b":"c"} }
, où l'objet dansa
est d'une autre classe et pas seulement d'un tableau associatif?Vous pouvez utiliser la bibliothèque Serializer de J ohannes Schmitt .
$serializer = JMS\Serializer\SerializerBuilder::create()->build(); $object = $serializer->deserialize($jsonData, 'MyNamespace\MyObject', 'json');
Dans la dernière version du sérialiseur JMS, la syntaxe est la suivante:
$serializer = SerializerBuilder::create()->build(); $object = $serializer->deserialize($jsonData, MyObject::class, 'json');
la source
::class
notation: php.net/manual/enVous pouvez créer un wrapper pour votre objet et donner l'impression que le wrapper est l'objet lui-même. Et cela fonctionnera avec des objets à plusieurs niveaux.
<?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
la source
Non, ce n'est pas possible depuis PHP 5.5.1.
La seule chose possible est d'avoir
json_decode
des tableaux associés de retour au lieu des objets StdClass.la source
Vous pouvez le faire de la manière ci-dessous.
<?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; } } } }
?>
Pour plus de détails, visitez create-custom-class-in-php-from-json-or-array
la source
Je suis surpris que personne n'ait encore mentionné cela.
Utilisez le composant Symfony Serializer: https://symfony.com/doc/current/components/serializer.html
Sérialisation d'un objet vers 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
Désérialisation de JSON à Object: (cet exemple utilise XML juste pour démontrer la flexibilité des formats)
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');
la source
Utiliser la réflexion :
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; }
la source
Comme le dit Gordon, ce n'est pas possible. Mais si vous cherchez un moyen d'obtenir une chaîne qui peut être décodée comme une instance d'une classe donnée, vous pouvez utiliser à la place sérialiser et désérialiser.
class Foo { protected $bar = 'Hello World'; function getBar() { return $this->bar; } } $string = serialize(new Foo); $foo = unserialize($string); echo $foo->getBar();
la source
J'ai créé une fois une classe de base abstraite à cet effet. Appelons cela JsonConvertible. Il doit sérialiser et désérialiser les membres publics. Ceci est possible en utilisant Reflection et la liaison statique tardive.
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();
Juste de mémoire, donc probablement pas parfait. Vous devrez également exclure les propriétés statiques et donner aux classes dérivées la possibilité de rendre certaines propriétés ignorées lorsqu'elles sont sérialisées vers / depuis json. J'espère que vous avez néanmoins l'idée.
la source
JSON est un protocole simple pour transférer des données entre différents langages de programmation (et c'est aussi un sous-ensemble de JavaScript) qui ne prend en charge que certains types: nombres, chaînes, tableaux / listes, objets / dictionnaires. Les objets ne sont que des cartes clé = valeur et les tableaux sont des listes ordonnées.
Il n'y a donc aucun moyen d'exprimer des objets personnalisés de manière générique. La solution consiste à définir une structure dans laquelle vos programmes sauront qu'il s'agit d'un objet personnalisé.
Voici un exemple:
{ "cls": "MyClass", fields: { "a": 123, "foo": "bar" } }
Cela pourrait être utilisé pour créer une instance de
MyClass
et définir les champsa
etfoo
à123
et"bar"
.la source
Je suis allé de l'avant et j'ai mis en œuvre la réponse de John Petit , en tant que fonction ( essentiel ):
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); }
Cela a parfaitement fonctionné pour mon cas d'utilisation. Cependant, la réponse de Yevgeniy Afanasyev me semble tout aussi prometteuse. Il est possible que votre classe ait un "constructeur" supplémentaire, comme ceci:
public static function withJson(string $json) { $instance = new static(); // Do your thing return $instance; }
Ceci est également inspiré par cette réponse .
la source
Je pense que le moyen le plus simple est:
function mapJSON($json, $class){ $decoded_object = json_decode($json); foreach ($decoded_object as $key => $value) { $class->$key = $value; } return $class;}
la source