从json对象创建php类树


Creating php classes tree from a json object

编辑

好吧,看来我真的不善于描述我的问题。我在网上找到了这个生成器,我正在寻找的是完全相同的东西,但用于php代码。知道吗?


原始问题

我愿意从json表示(API映射到对象)构建许多php类,为此我想转换为:

{
"success": true,
"domains": [
  {
     "id": "13",
     "manual": "0",
     "name": "silo3.mobi",
     "lastname": "Doe",
     "firstname": "John",
     "cid": "1",
     "period": "1",
     "recurring_amount": "9.95",
     "currency_id": "0",
     "module": "namesilo",
     "next_due": "2012-12-12",
     "expires": "2012-12-12",
     "status": "Active",
     "type": "Register",
     "date_created": "2011-12-12",
     "autorenew": "1",
     "reglock": "1",
     "idprotection": "1"
  },
  {
     "id": "11",
     "manual": "0",
     "name": "netearthorg.org",
     "lastname": "Doe",
     "firstname": "John",
     "cid": "1",
     "period": "1",
     "recurring_amount": "9.95",
     "currency_id": "0",
     "module": "NetEarthOne",
     "next_due": "2012-11-22",
     "expires": "2012-11-22",
     "status": "Active",
     "type": "Register",
     "date_created": "2011-11-22",
     "autorenew": "1",
     "reglock": "1",
     "idprotection": "0"
  },
  {
     "id": "10",
     "manual": "0",
     "name": "hbappreseller.co.uk",
     "lastname": "Blue",
     "firstname": "Mike",
     "cid": "6",
     "period": "2",
     "recurring_amount": "9.95",
     "currency_id": "0",
     "module": "NetEarthOne",
     "next_due": "2012-11-22",
     "expires": "0000-00-00",
     "status": "Pending",
     "type": "Register",
     "date_created": "0000-00-00",
     "autorenew": "1",
     "reglock": "0",
     "idprotection": "0"
  }
],
"call": "getDomains",
"server_time": 1323793581
}

到具有bool:success属性的对象、"域"对象的数组等等

这并不难,我可以自己开发,但我想知道是否有一些php-lib可以处理这个问题,还没有找到任何

编辑

好吧,我还没有很好地解释我自己,我想我想做的是构建一个php类文件,并依赖于其他类等等,这样我就可以匹配json结构。

例如,给定的json应该生成以下内容:

class Domain {
    protected $id;
    protected $manual;
    protected $name;
    protected $lastname;
    protected $firstname;
    protected $cid;
    protected $period;
    protected $recurring_amount;
    // and so on
}

其目的是为具有复杂对象的WSDL提供服务,并避免在对原始API进行任何修改的情况下使WSDL签名进化(自定义类不会经常更改,只有在需要时才会更改,以便WSDL保持不变)

api生成数百个json对象,其中一些对象共享属性,因此这样做的目的是使用全局方法来处理所有json字符串并构建或获取构建的对象,例如,两个json可以具有"domains"属性,因此,当我第一次想要生成一个名为Domain的类时(如果属性=数组,则创建属性名为-S的文件并填充属性,然后保存到文件中以供进一步使用)

假设您的JSON对象存储在$json中,那么您可以像这样动态创建一个类-

$data = json_decode($json, true);
$class = new Domain();
foreach ($data AS $key => $value) $class->{$key} = $value;

如果你想要一种更通用的方法,比如说你想在飞行中更改类名-

$data = json_decode($json, true);
$className = "Domain"; // Or assign it to something else like pick from DB, from JSON from anywhere.
$class = new {$className}();
foreach ($data AS $key => $value) $class->{$key} = $value;

好的,最后我找不到任何东西来做json2csharp工具的工作,所以我开发了我的:

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'));
    }
}

这段代码非常适合我的需求,但它可以很容易地适应任何json文件,结果是:

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

}

生成的文件(缺少文档块,但将进行集成,以便正确提供WSDL)

ClientResponse.php(基本对象)

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;
}

我制作了一个PHP类生成器,它将使用JSON生成模型https://json2php.strikebit.io/.它将递归地检查JSON并生成相应的类。

在我看来,不应该为这样的通用数据创建对象。您可以很容易地将其映射到通用数据对象。

因此,您的框架将只是标准的PHP。类似:

class JsonObject
{
    protected $data = array();
    public function __construct($data)
    {
        $this->data = $data;
    }
    public function __get($var)
    {
        if (array_key_exists($var, $this->data)) {
            return $this->data[$var];
        } else {
            throw new Exception($var . ' not found in ' . __CLASS__);
        }
    }
    public function __set($var, $val)
    {
        if (array_key_exists($var, $this->data)) {
            return $this->data[$var];
        } else {
            throw new Exception($var . ' not found in ' . __CLASS__);
        }
    }

}
class Domain extends JsonObject
{
    //some domain specific functionality
}
class getDomainResult
{
    public $domains = array();
    public $success = false;
    public $lastTime = 0;
    //some methods to do the calls
    public function callback($result)
    {
        $res = json_decode($result, true);
        $this->success = $res['success'];
        $this->lastTime = $res['server_time'];
        foreach ($res['domains'] as $domain) {
            $this->domains[] = new Domain($domain);
        }
    }
}