JSON_Decode to Custom Class

Creating php classes tree from a json object

Ok, finally I found nothing to do the job of json2csharp tool, so I developed mine:

namespace Hostbill\Api\Generator;

use Zend\Code\Generator\ClassGenerator;
use Zend\Code\Generator\PropertyValueGenerator;
use Zend\Code\Reflection\ClassReflection;
use Zend\Json\Json;
use Zend\Json\Exception\RuntimeException as JsonRuntimeException;

class DataGenerator extends AbstractGenerator
{
const DATA_NAMESPACE = 'Hostbill\Api\Data';
const RESPONSE_SUFFIX = 'Response';
const DATA_ABSTRACT_CLASS = 'AbstractData';

/**
* @var ClassGenerator[]
*/
protected $classes = array();

/**
* @var ClassGenerator
*/
protected $responseClass;

/**
* Build classes from a source json string
* @param string $json
*/
public function fromSource($json)
{
try {
$data = Json::decode($json, Json::TYPE_ARRAY);
} catch (JsonRuntimeException $e) {
$this->err(sprintf('Could not generate classes for given Json, err:"%s"', $e->getMessage()));
return;
}

$this->parse($data);

// write classes files
$this->write($this->responseClass, sprintf('%s/../Data/', __DIR__));

foreach ($this->classes as $class) {
if (self::RESPONSE_SUFFIX === substr($class->getName(), -strlen(self::RESPONSE_SUFFIX))) {
$this->write($class, sprintf('%s/../Data/Response/', __DIR__));
} else {
$this->write($class, sprintf('%s/../Data/', __DIR__));
}
}
}

/**
* Parse json decoded object and generate corresponding classes
* @param array $data associative array retrieved from json_decode
* @return DataGenerator
*/
public function parse($data)
{
$responseClassNamespace = sprintf('%s\%s', self::DATA_NAMESPACE, self::RESPONSE_SUFFIX);

// get "call" property and build Response class name on it: getClientDetails => ClientDetailResponse
$parts = preg_split('/(?=[A-Z])/', $data['call'], -1, PREG_SPLIT_NO_EMPTY);
array_shift($parts); // remove verb
$parts[] = $this->inflector()->singularize(array_pop($parts));
$parts[] = self::RESPONSE_SUFFIX;
$baseResponseClassName = sprintf('%s\%s', self::DATA_NAMESPACE, self::RESPONSE_SUFFIX);
$responseClass = new ClassGenerator(
implode('', $parts),
$responseClassNamespace,
null,
self::RESPONSE_SUFFIX
);
$responseClass->addUse($baseResponseClassName);
$this->addClass($responseClass);

if (!class_exists($baseResponseClassName)) {
$baseResponseClassGenerated = true;
$baseResponseClass = new ClassGenerator(
self::RESPONSE_SUFFIX,
self::DATA_NAMESPACE,
ClassGenerator::FLAG_ABSTRACT
);
} else {
$baseResponseClassGenerated = false;
$baseResponseClass = ClassGenerator::fromReflection(new ClassReflection($baseResponseClassName));
}
$this->responseClass = $baseResponseClass;

foreach ($data as $key => $value) {
$key = $this->inflector()->pascalize($key);
if (is_scalar($value)) {
// thoses properties belongs to the response class
// if we just have generated the "base" response class (Response.php)
// store properties there (there are only 3 basic properties: success, call, serverTime)
// otherwise store them in the child response class, but avoid any overriding of the
// 3 properties which are stored in base Response class
if ($baseResponseClassGenerated) {
$responseClassToUpdate = $baseResponseClass;
} else {
$responseClassToUpdate = $responseClass;
}
// update base response class
if (!$responseClassToUpdate->hasProperty($key) && !$baseResponseClass->hasProperty($key)) {
$responseClassToUpdate->addProperty($key);
}
} else {
// object
if ($this->isArrayAssociative($value)) {
if (!$responseClass->hasProperty($key)) {
$responseClass->addProperty($key);
}
$this->parseObject($key, $value);

// array
} else {
if (!$responseClass->hasProperty($key)) {
$responseClass->addProperty($key, new PropertyValueGenerator(array(), PropertyValueGenerator::TYPE_ARRAY));
}

// if array is simple array, do nothing
if (!is_scalar(reset($value))) {
$this->parseArrayOfObjects($key, $value);
}
}
}
}
return $this;
}

/**
* Parse ordered array and create class object
* @param string $name key name
* @param array $data
* @return DataGenerator
*/
public function parseArrayOfObjects($name, $data)
{
$class = $this->getOrCreateClass($this->inflector()->singularize($name));

foreach ($data as $object) {
foreach ($object as $key => $value) {
if (!$class->hasProperty($key)) {
$class->addProperty($key);
}
}
}

return $this;
}

/**
* Parse associative array and create class object
* @param string $name key name
* @param array $data
* @return DataGenerator
*/
public function parseObject($name, $data)
{
$class = $this->getOrCreateClass($this->inflector()->singularize($name));

foreach ($data as $key => $value) {
if (!$class->hasProperty($key)) {
$class->addProperty($key);
}
}

return $this;
}

/**
* Add class to current stack
* @param ClassGenerator $class
* @return DataGenerator
*/
protected function addClass(ClassGenerator $class)
{
$this->classes[$this->inflector()->lowerize($class->getName())] = $class;
return $this;
}

/**
* Get class from current stack
* @param string $name
* @return false|ClassGenerator False if not found
*/
protected function getClass($name)
{
$id = $this->inflector()->lowerize($name);
if (!isset($this->classes[$id])) {
return false;
}
return $this->classes[$id];
}

/**
* Try to retrievea class from current stack, create it if not found
* @param string $name
* @return ClassGenerator
*/
protected function getOrCreateClass($name)
{
if (!$class = $this->getClass($name)) {
$class = new ClassGenerator(
$this->inflector()->camelize($name),
self::DATA_NAMESPACE,
null,
self::DATA_ABSTRACT_CLASS
);
$this->addClass($class);
}
return $class;
}

/**
* Check if the given array is associative
* @param array $array
* @return bool
*/
protected function isArrayAssociative($array)
{
return (bool)count(array_filter(array_keys($array), 'is_string'));
}
}

This code is so oriented for my needs, but it can easily be adapted to any json file, here the result:

JSON

  {
"success": true,
"client": {
"id": "1",
"email": "jondoe@email.com",
"password": "474bf122c92de249ace867a003cb7196",
"lastlogin": "2011-11-25 04:32:40",
"ip": "213.54.21.3",
"host": "cmt-random.uk",
"status": "Active",
"parent_id": "0",
"firstname": "John",
"lastname": "Doe",
"companyname": "",
"address1": "Address 54",
"address2": "",
"city": "Soullans",
"state": "Birmingham",
"postcode": "B33 8TH",
"country": "GB",
"phonenumber": "357755733",
"datecreated": "2011-09-24",
"notes": "",
"language": "spanish",
"company": "0",
"credit": "0.00",
"taxexempt": "0",
"latefeeoveride": "0",
"cardtype": "Visa",
"cardnum": null,
"expdate": null,
"overideduenotices": "0",
"client_id": "1",
"currency_id": "0",
"countryname": "United Kingdom"
},
"call": "getClientDetails",
"server_time": 1323442995

}

GENERATED FILES (docblocks are missing but will be integrated so the WSDL is served correctly)

ClientResponse.php (base object)

namespace Hostbill\Api\Data\Response;

use Hostbill\Api\Data\Response;

class ClientResponse extends Response
{

public $clientId = null;

public $info = array(

);

}

Client.php

namespace Hostbill\Api\Data;

class Client extends AbstractData
{

public $id = null;

public $email = null;

public $password = null;

public $lastlogin = null;

public $ip = null;

public $host = null;

public $status = null;

public $parent_id = null;

public $firstname = null;

public $lastname = null;

public $companyname = null;

public $address1 = null;

public $address2 = null;

public $city = null;

public $state = null;

public $postcode = null;

public $country = null;

public $phonenumber = null;

public $datecreated = null;

public $notes = null;

public $language = null;

public $company = null;

public $credit = null;

public $taxexempt = null;

public $latefeeoveride = null;

public $cardtype = null;

public $cardnum = null;

public $expdate = null;

public $overideduenotices = null;

public $client_id = null;

public $currency_id = null;

public $countryname = null;

public $services = null;
}

How to Deserialize a list of objects from json in flutter

Well, your service would handle either the response body being a map, or a list of maps accordingly. Based on the code you have, you are accounting for 1 item.

If the response body is iterable, then you need to parse and walk accordingly, if I am understanding your question correctly.

Example:

Iterable l = json.decode(response.body);
List<Post> posts = List<Post>.from(l.map((model)=> Post.fromJson(model)));

where the post is a LIST of posts.

EDIT: I wanted to add a note of clarity here. The purpose here is that you decode the response returned. The next step, is to turn that iterable of JSON objects into an instance of your object. This is done by creating fromJson methods in your class to properly take JSON and implement it accordingly. Below is a sample implementation.

class Post {
// Other functions and properties relevant to the class
// ......
/// Json is a Map<dynamic,dynamic> if i recall correctly.
static fromJson(json): Post {
Post p = new Post()
p.name = ...
return p
}
}

I am a bit abstracted from Dart these days in favor of a better utility for the tasks needing to be accomplished. So my syntax is likely off just a little, but this is Pseudocode.



Related Topics



Leave a reply



Submit