从json对象创建php类树

时间:2014-09-23 09:50:52

标签: php json code-generation

修改

好吧,我似乎很难描述我的问题。我在网上找到了this generator,而我正在寻找的东西与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库可以解决这个问题,还没有发现任何

修改

好的我没有解释得这么好我想,我想做的是构建一个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可以拥有“域”属性,所以我第一次要生成一个名为Domain的类(如果property = array然后创建具有属性名称-S的文件并填充属性,然后保存到文件以供进一步使用)

4 个答案:

答案 0 :(得分:3)

假设您的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;

答案 1 :(得分:1)

好的,最后我发现没有什么可以做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

}

生成的文件(缺少docblock但会集成以便正确提供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;
}

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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

所以你的框架只是标准的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);
        }
    }
}