Мне нравятся ваши предложения, просто хочу отметить, что он не будет работать с вложенными объектами (кроме STDClass или преобразованного объекта)
javier_domenech
34
Мы создали JsonMapper для автоматического сопоставления объектов JSON с нашими собственными классами модели. Он отлично работает с вложенными / дочерними объектами.
Он полагается только на информацию о типе докблока для сопоставления, которая в любом случае есть у большинства свойств класса:
<?php
$mapper = new JsonMapper();
$contactObject = $mapper->map(
json_decode(file_get_contents('http://example.org/contact.json')),
new Contact()
);
?>
Вы можете объяснить лицензию OSL3? Если я использую JsonMapper на веб-сайте, должен ли я выпустить исходный код этого веб-сайта? Если я использую JsonMapper в коде на устройстве, которое я продаю, должен ли весь код этого устройства быть открытым?
EricP
Нет, вам нужно только опубликовать изменения, которые вы вносите в сам JsonMapper.
cweiske
29
Вы можете это сделать - это беспорядок, но вполне возможно. Пришлось делать, когда мы начали складывать вещи на диване.
$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
В наших тестах это было намного быстрее, чем пытаться перебрать все переменные класса.
Предостережение: не будет работать для вложенных объектов, кроме stdClass
Изменить: помните об источнике данных, настоятельно рекомендуется не делать этого с ненадежными данными от пользователей без очень тщательного анализа рисков.
Работает ли это с инкапсулированными подклассами. Например { "a": {"b":"c"} }, где объект aотносится к другому классу, а не только к ассоциативному массиву?
J-Rou
2
нет, json_decode создает объекты stdclass, в том числе подобъекты, если вы хотите, чтобы они были чем-то еще, вам нужно убрать каждый объект, как указано выше.
Джон Петтитт
Спасибо, вот что я представил
J-Rou
Как насчет использования этого решения для объектов, у которых конструктор имеет параметры. Я не могу заставить его работать. Я надеялся, что кто-то может указать мне правильное направление, чтобы это решение работало с объектом, который имеет настраиваемый конструктор с параметрами.
Синтаксис не зависит от версии JMS Serializer, а скорее от версии PHP - начиная с PHP5.5 вы можете использовать ::classнотацию: php.net/manual/en/…
Иван Ярыч
4
Вы можете сделать оболочку для своего объекта и сделать так, чтобы оболочка выглядела так, как будто это сам объект. И работать с многоуровневыми объектами.
<?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
Десериализация из JSON в Object: (в этом примере XML используется только для демонстрации гибкости форматов)
Как говорит Гордон, это невозможно. Но если вы ищете способ получить строку, которая может быть декодирована как экземпляр класса give, вы можете вместо этого использовать сериализацию и десериализацию.
Кажется, это не решает вопрос. Если это так, вы должны предоставить некоторые объяснения.
Феликс Клинг,
1
Однажды я создал для этой цели абстрактный базовый класс. Назовем его JsonConvertible. Он должен сериализовать и десериализовать публичные члены. Это возможно с помощью Reflection и позднего статического связывания.
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();
Просто по памяти, так что наверное не безупречно. Вам также придется исключить статические свойства и дать производным классам возможность игнорировать некоторые свойства при сериализации в / из json. Тем не менее, надеюсь, вы уловили идею.
JSON - это простой протокол для передачи данных между различными языками программирования (а также подмножество JavaScript), который поддерживает только определенные типы: числа, строки, массивы / списки, объекты / словари. Объекты - это просто карты ключ = значение, а массивы - это упорядоченные списки.
Таким образом, нет возможности выразить настраиваемые объекты в общем виде. Решение заключается в определении структуры, в которой ваша программа (программы) будет знать, что это настраиваемый объект.
Это может быть правдой, но вопрос не в том, чтобы представлять объекты в общем виде. Похоже, у него есть конкретный пакет JSON, который сопоставляется с определенным классом на одном или обоих концах. Нет причин, по которым вы не можете использовать JSON в качестве явной сериализации таким образом неуниверсальных именованных классов. Назвать его так, как вы это делаете, - это нормально, если вам нужно общее решение, но также нет ничего плохого в наличии согласованного контракта по структуре JSON.
DougW
Это может сработать, если вы реализуете Serializable на стороне кодирования и имеете условные обозначения на стороне декодирования. Может даже работать с подклассами при правильной организации.
Это отлично сработало для моего варианта использования. Однако ответ Евгения Афанасьева мне кажется не менее многообещающим. Возможно, у вашего класса будет дополнительный «конструктор», например:
publicstaticfunctionwithJson(string $json) {
$instance = newstatic();
// Do your thingreturn $instance;
}
Ответы:
Не автоматически. Но вы можете сделать это по старинному маршруту.
$data = json_decode($json, true); $class = new Whatever(); foreach ($data as $key => $value) $class->{$key} = $value;
Или, как вариант, вы можете сделать это более автоматическим:
class Whatever { public function set($data) { foreach ($data AS $key => $value) $this->{$key} = $value; } } $class = new Whatever(); $class->set($data);
Изменить : становится немного интереснее:
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);
источник
Мы создали JsonMapper для автоматического сопоставления объектов JSON с нашими собственными классами модели. Он отлично работает с вложенными / дочерними объектами.
Он полагается только на информацию о типе докблока для сопоставления, которая в любом случае есть у большинства свойств класса:
<?php $mapper = new JsonMapper(); $contactObject = $mapper->map( json_decode(file_get_contents('http://example.org/contact.json')), new Contact() ); ?>
источник
Вы можете это сделать - это беспорядок, но вполне возможно. Пришлось делать, когда мы начали складывать вещи на диване.
$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
В наших тестах это было намного быстрее, чем пытаться перебрать все переменные класса.
Предостережение: не будет работать для вложенных объектов, кроме stdClass
Изменить: помните об источнике данных, настоятельно рекомендуется не делать этого с ненадежными данными от пользователей без очень тщательного анализа рисков.
источник
{ "a": {"b":"c"} }
, где объектa
относится к другому классу, а не только к ассоциативному массиву?Вы можете использовать библиотеку сериализатора Йоханнеса Шмитта .
$serializer = JMS\Serializer\SerializerBuilder::create()->build(); $object = $serializer->deserialize($jsonData, 'MyNamespace\MyObject', 'json');
В последней версии сериализатора JMS синтаксис:
$serializer = SerializerBuilder::create()->build(); $object = $serializer->deserialize($jsonData, MyObject::class, 'json');
источник
::class
нотацию: php.net/manual/en/…Вы можете сделать оболочку для своего объекта и сделать так, чтобы оболочка выглядела так, как будто это сам объект. И работать с многоуровневыми объектами.
<?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
источник
Нет, в PHP 5.5.1 это невозможно.
Единственное, что возможно, - это иметь
json_decode
возвращаемые ассоциированные массивы вместо объектов StdClass.источник
Вы можете сделать это следующим образом ..
<?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; } } } }
?>
Для получения дополнительной информации посетите create-custom-class-in-php-from-json-or-array.
источник
Я удивлен, что пока об этом никто не упомянул.
Используйте компонент Symfony Serializer: https://symfony.com/doc/current/components/serializer.html
Сериализация от объекта к 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
Десериализация из JSON в Object: (в этом примере XML используется только для демонстрации гибкости форматов)
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');
источник
Использовать отражение :
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; }
источник
Как говорит Гордон, это невозможно. Но если вы ищете способ получить строку, которая может быть декодирована как экземпляр класса give, вы можете вместо этого использовать сериализацию и десериализацию.
class Foo { protected $bar = 'Hello World'; function getBar() { return $this->bar; } } $string = serialize(new Foo); $foo = unserialize($string); echo $foo->getBar();
источник
Однажды я создал для этой цели абстрактный базовый класс. Назовем его JsonConvertible. Он должен сериализовать и десериализовать публичные члены. Это возможно с помощью Reflection и позднего статического связывания.
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();
Просто по памяти, так что наверное не безупречно. Вам также придется исключить статические свойства и дать производным классам возможность игнорировать некоторые свойства при сериализации в / из json. Тем не менее, надеюсь, вы уловили идею.
источник
JSON - это простой протокол для передачи данных между различными языками программирования (а также подмножество JavaScript), который поддерживает только определенные типы: числа, строки, массивы / списки, объекты / словари. Объекты - это просто карты ключ = значение, а массивы - это упорядоченные списки.
Таким образом, нет возможности выразить настраиваемые объекты в общем виде. Решение заключается в определении структуры, в которой ваша программа (программы) будет знать, что это настраиваемый объект.
Вот пример:
{ "cls": "MyClass", fields: { "a": 123, "foo": "bar" } }
Это можно использовать для создания экземпляра
MyClass
и установки полейa
иfoo
в значения123
и"bar"
.источник
Я пошел дальше и реализовал ответ Джона Пети как функцию ( суть ):
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); }
Это отлично сработало для моего варианта использования. Однако ответ Евгения Афанасьева мне кажется не менее многообещающим. Возможно, у вашего класса будет дополнительный «конструктор», например:
public static function withJson(string $json) { $instance = new static(); // Do your thing return $instance; }
Это тоже навеяно этим ответом .
источник
Думаю, самый простой способ:
function mapJSON($json, $class){ $decoded_object = json_decode($json); foreach ($decoded_object as $key => $value) { $class->$key = $value; } return $class;}
источник