Você pode explicar a licença OSL3? Se eu usar o JsonMapper em um site, devo liberar o código-fonte desse site? Se eu usar o JsonMapper no código de um dispositivo que vendo, todo o código desse dispositivo deve ser de código aberto?
EricP
Não, você só precisa publicar as alterações feitas no próprio JsonMapper.
cweiske
29
Você pode fazer isso - é uma confusão, mas totalmente possível. Tivemos que fazer quando começamos a armazenar coisas no 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
Em nossos benchmarks, isso foi muito mais rápido do que tentar iterar por todas as variáveis de classe.
Advertência: não funciona para objetos aninhados diferentes de stdClass
Editar: tenha em mente a fonte de dados, é altamente recomendável que você não faça isso com dados não confiáveis de usuários sem uma análise cuidadosa dos riscos.
Isso funciona com subclasses encapsuladas. Por exemplo { "a": {"b":"c"} }, onde o objeto in aé de outra classe e não apenas um array associativo?
J-Rou
2
não, json_decode cria objetos stdclass, incluindo subobjetos, se você quiser que sejam qualquer outra coisa, você terá que fazer o kludge de cada objeto como acima.
John Pettitt de
Obrigado, isso é o que imaginei
J-Rou
Que tal usar essa solução em objetos onde o construtor possui parâmetros. Eu não posso fazê-lo funcionar. Eu esperava que alguém pudesse me apontar a direção certa para fazer essa solução funcionar com um objeto que tem um construtor personalizado com parâmetros.
<?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
Desserializando de JSON para objeto: (este exemplo usa XML apenas para demonstrar a flexibilidade dos formatos)
Como diz Gordon, não é possível. Mas se você está procurando uma maneira de obter uma string que possa ser decodificada como uma instância de uma determinada classe, você pode usar serializar e desserializar.
Isso não parece responder à questão. Em caso afirmativo, você deve fornecer alguma explicação.
Felix Kling
1
Certa vez, criei uma classe base abstrata para esse propósito. Vamos chamá-lo de JsonConvertible. Ele deve serializar e desserializar os membros públicos. Isso é possível usando Reflection e late static binding.
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();
Só de memória, provavelmente não é perfeito. Você também terá que excluir as propriedades estáticas e pode dar às classes derivadas a chance de fazer com que algumas propriedades sejam ignoradas quando serializadas de / para json. Espero que você tenha a ideia, no entanto.
JSON é um protocolo simples para transferir dados entre várias linguagens de programação (e também é um subconjunto de JavaScript) que suporta apenas alguns tipos: números, strings, arrays / listas, objetos / dicts. Os objetos são apenas mapas chave = valor e os Arrays são listas ordenadas.
Portanto, não há como expressar objetos personalizados de maneira genérica. A solução é definir uma estrutura onde seu (s) programa (s) saberão que é um objeto personalizado.
Isso pode ser verdade, mas a questão não é a de representar objetos de maneira genérica. Parece que ele tem uma bolsa JSON específica que mapeia para uma classe específica em uma ou ambas as extremidades. Não há motivo para não usar JSON como uma serialização explícita de classes nomeadas não genéricas dessa maneira. Nomear como você está fazendo é bom se você quiser uma solução genérica, mas também não há nada de errado em ter um contrato acordado na estrutura JSON.
DougW
Isso pode funcionar se você implementar Serializable no final da codificação e tiver condicionais no final da decodificação. Pode até funcionar com subclasses se organizado corretamente.
Isso funcionou perfeitamente para o meu caso de uso. No entanto, a resposta de Yevgeniy Afanasyev parece igualmente promissora para mim. Pode ser possível que sua classe tenha um "construtor" extra, como:
publicstaticfunctionwithJson(string $json) {
$instance = newstatic();
// Do your thingreturn $instance;
}
Respostas:
Não automaticamente. Mas você pode fazer isso da maneira antiga.
$data = json_decode($json, true); $class = new Whatever(); foreach ($data as $key => $value) $class->{$key} = $value;
Ou, alternativamente, você pode tornar isso mais automático:
class Whatever { public function set($data) { foreach ($data AS $key => $value) $this->{$key} = $value; } } $class = new Whatever(); $class->set($data);
Edit : ficando um pouco mais sofisticado:
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);
fonte
Construímos JsonMapper para mapear objetos JSON em nossas próprias classes de modelo automaticamente. Ele funciona bem com objetos aninhados / filhos.
Ele depende apenas das informações do tipo docblock para mapeamento, que a maioria das propriedades de classe possui:
<?php $mapper = new JsonMapper(); $contactObject = $mapper->map( json_decode(file_get_contents('http://example.org/contact.json')), new Contact() ); ?>
fonte
Você pode fazer isso - é uma confusão, mas totalmente possível. Tivemos que fazer quando começamos a armazenar coisas no 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
Em nossos benchmarks, isso foi muito mais rápido do que tentar iterar por todas as variáveis de classe.
Advertência: não funciona para objetos aninhados diferentes de stdClass
Editar: tenha em mente a fonte de dados, é altamente recomendável que você não faça isso com dados não confiáveis de usuários sem uma análise cuidadosa dos riscos.
fonte
{ "a": {"b":"c"} }
, onde o objeto ina
é de outra classe e não apenas um array associativo?Você pode usar a biblioteca Serializer de J ohannes Schmitt .
$serializer = JMS\Serializer\SerializerBuilder::create()->build(); $object = $serializer->deserialize($jsonData, 'MyNamespace\MyObject', 'json');
Na versão mais recente do serializador JMS, a sintaxe é:
$serializer = SerializerBuilder::create()->build(); $object = $serializer->deserialize($jsonData, MyObject::class, 'json');
fonte
::class
notação: php.net/manual/en/…Você pode fazer um invólucro para o seu objeto e fazer com que ele pareça ser o próprio objeto. E funcionará com objetos de vários níveis.
<?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
fonte
Não, isso não é possível a partir do PHP 5.5.1.
A única coisa possível é
json_decode
retornar matrizes associadas em vez de objetos StdClass.fonte
Você pode fazer isso da maneira abaixo ..
<?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 obter mais detalhes, visite create-custom-class-in-php-from-json-or-array
fonte
Estou surpreso que ninguém tenha mencionado isso ainda.
Use o componente Symfony Serializer: https://symfony.com/doc/current/components/serializer.html
Serializando do objeto para 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
Desserializando de JSON para objeto: (este exemplo usa XML apenas para demonstrar a flexibilidade dos 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');
fonte
Use reflexão :
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; }
fonte
Como diz Gordon, não é possível. Mas se você está procurando uma maneira de obter uma string que possa ser decodificada como uma instância de uma determinada classe, você pode usar serializar e desserializar.
class Foo { protected $bar = 'Hello World'; function getBar() { return $this->bar; } } $string = serialize(new Foo); $foo = unserialize($string); echo $foo->getBar();
fonte
Certa vez, criei uma classe base abstrata para esse propósito. Vamos chamá-lo de JsonConvertible. Ele deve serializar e desserializar os membros públicos. Isso é possível usando Reflection e late static binding.
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();
Só de memória, provavelmente não é perfeito. Você também terá que excluir as propriedades estáticas e pode dar às classes derivadas a chance de fazer com que algumas propriedades sejam ignoradas quando serializadas de / para json. Espero que você tenha a ideia, no entanto.
fonte
JSON é um protocolo simples para transferir dados entre várias linguagens de programação (e também é um subconjunto de JavaScript) que suporta apenas alguns tipos: números, strings, arrays / listas, objetos / dicts. Os objetos são apenas mapas chave = valor e os Arrays são listas ordenadas.
Portanto, não há como expressar objetos personalizados de maneira genérica. A solução é definir uma estrutura onde seu (s) programa (s) saberão que é um objeto personalizado.
Aqui está um exemplo:
{ "cls": "MyClass", fields: { "a": 123, "foo": "bar" } }
Isso pode ser usado para criar uma instância de
MyClass
e definir os camposa
efoo
para123
e"bar"
.fonte
Fui em frente e implementei a resposta de John Petit , como uma função ( essência ):
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); }
Isso funcionou perfeitamente para o meu caso de uso. No entanto, a resposta de Yevgeniy Afanasyev parece igualmente promissora para mim. Pode ser possível que sua classe tenha um "construtor" extra, como:
public static function withJson(string $json) { $instance = new static(); // Do your thing return $instance; }
Isso também é inspirado por esta resposta .
fonte
Acho que a maneira mais simples é:
function mapJSON($json, $class){ $decoded_object = json_decode($json); foreach ($decoded_object as $key => $value) { $class->$key = $value; } return $class;}
fonte