¿Es posible decodificar una cadena json en un objeto que no sea stdClass?
¿Es posible decodificar una cadena json en un objeto que no sea stdClass?
Respuestas:
No de forma automática. Pero puedes hacerlo por la ruta antigua.
$data = json_decode($json, true);
$class = new Whatever();
foreach ($data as $key => $value) $class->{$key} = $value;
O, alternativamente, podría hacerlo más automático:
class Whatever {
public function set($data) {
foreach ($data AS $key => $value) $this->{$key} = $value;
}
}
$class = new Whatever();
$class->set($data);
Editar : volviéndose un poco más elegante:
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);
Creamos JsonMapper para mapear objetos JSON en nuestras propias clases de modelo automáticamente. Funciona bien con objetos anidados / secundarios.
Solo se basa en la información de tipo docblock para el mapeo, que la mayoría de las propiedades de clase tienen de todos modos:
<?php
$mapper = new JsonMapper();
$contactObject = $mapper->map(
json_decode(file_get_contents('http://example.org/contact.json')),
new Contact()
);
?>
Puedes hacerlo, es un torpe pero totalmente posible. Tuvimos que hacer cuando empezamos a almacenar cosas en la base del sofá.
$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
En nuestros puntos de referencia, esto fue mucho más rápido que intentar iterar a través de todas las variables de clase.
Advertencia: no funcionará para objetos anidados que no sean stdClass
Editar: tenga en cuenta la fuente de datos, se recomienda encarecidamente que no haga esto con datos no confiables de los usuarios sin un análisis muy cuidadoso de los riesgos.
{ "a": {"b":"c"} }
, ¿dónde el objeto a
es de otra clase y no solo una matriz asociativa?
Puede utilizar la biblioteca Serializer de J ohannes Schmitt .
$serializer = JMS\Serializer\SerializerBuilder::create()->build();
$object = $serializer->deserialize($jsonData, 'MyNamespace\MyObject', 'json');
En la última versión del serializador JMS, la sintaxis es:
$serializer = SerializerBuilder::create()->build();
$object = $serializer->deserialize($jsonData, MyObject::class, 'json');
::class
notación: php.net/manual/en/…
Puede hacer una envoltura para su objeto y hacer que la envoltura parezca que es el objeto en sí. Y funcionará con objetos multinivel.
<?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
No, esto no es posible a partir de PHP 5.5.1.
Lo único posible es tener json_decode
matrices asociadas de retorno en lugar de los objetos StdClass.
Puedes hacerlo de la siguiente manera.
<?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;
}
}
}
}
?>
Para obtener más detalles, visite create-custom-class-in-php-from-json-or-array
Me sorprende que nadie haya mencionado esto todavía.
Utilice el componente Serializador de Symfony: https://symfony.com/doc/current/components/serializer.html
Serializando de 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
Deserialización de JSON a Object: (este ejemplo usa XML solo para demostrar la flexibilidad de los formatos)
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');
Utilice Reflexión :
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;
}
Como dice Gordon, no es posible. Pero si está buscando una forma de obtener una cadena que pueda decodificarse como una instancia de una clase determinada, puede usar serializar y unserializar en su lugar.
class Foo
{
protected $bar = 'Hello World';
function getBar() {
return $this->bar;
}
}
$string = serialize(new Foo);
$foo = unserialize($string);
echo $foo->getBar();
Una vez creé una clase base abstracta para este propósito. Llamémoslo JsonConvertible. Debe serializar y deserializar los miembros públicos. Esto es posible usando Reflection y enlace estático tardío.
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 de memoria, probablemente no sea impecable. También tendrá que excluir las propiedades estáticas y puede dar a las clases derivadas la oportunidad de hacer que algunas propiedades se ignoren cuando se serializan a / desde json. No obstante, espero que entiendas la idea.
JSON es un protocolo simple para transferir datos entre varios lenguajes de programación (y también es un subconjunto de JavaScript) que admite solo ciertos tipos: números, cadenas, matrices / listas, objetos / dictados. Los objetos son simplemente mapas clave = valor y las matrices son listas ordenadas.
Por tanto, no hay forma de expresar objetos personalizados de forma genérica. La solución es definir una estructura en la que su (s) programa (s) sabrán que es un objeto personalizado.
He aquí un ejemplo:
{ "cls": "MyClass", fields: { "a": 123, "foo": "bar" } }
Esto podría usarse para crear una instancia de MyClass
y establecer los campos a
y foo
para 123
y "bar"
.
Seguí adelante e implementé la respuesta de John Petit , como una función ( esencia ):
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);
}
Esto funcionó perfectamente para mi caso de uso. Sin embargo, la respuesta de Yevgeniy Afanasyev me parece igualmente prometedora. Podría ser posible que su clase tenga un "constructor" adicional, así:
public static function withJson(string $json) {
$instance = new static();
// Do your thing
return $instance;
}
Esto también se inspira en esta respuesta .