Restful API中的JSON模板解析-JSONOut库的介绍

项目地址

Restful API中的JSON模板解析

更新在2020-12-14:增加嵌套结构的支持。

为什么需要JSON模板解析

Restful API的两个重要因素是输入和输出,一个好的API要求输入和输出都清晰可见。
输入包括用户提交的方式、参数等,都是可预期的。尽量不要采用数组接收。例如以下的例子,

$data = $_POST;
$User->save($data);

这样的坏处一是无法过滤用户非法、多余的参数提交;二是参数不明,后续无法维护。

输出则要求仅提供满足需要的最小的数据集。例如如下的例子,

$user = User::get(1);
echo $user->toJson();

这样的坏处一是输出的内容不清晰,只有知道数据表的结构才能了解输出的内容;二是可能一不小心把用户表敏感字段(密码,手机号)输出,造成信息泄漏。

JSONOut库介绍

解决输出的方法,是采用JSON模板解析的方法。例如用户表有id,name,pass,avatar,phone字段,其中只有name,avatar是需要输出的。那么模板可以这样定义:

{
  "name": "",
  "avatar": ""
}

当输出用户数据时,仅保留name和avatar,其它字段自动过滤掉。

优点是:

  • 输出结构一目了然
  • 仅保留满足需要的最小的数据集
  • 敏感字段自动过滤,减少信息泄露

算法代码如下:

class JSONOut
{
    /**
    *按照JSON模板输出JSON数据
    *@param data 源数据
    *@param tplContent JSON模板内容
    *@param multi 是否为多条内容,默认单条
    *@return string
    */
    public static function toJSON($data, $tplContent,$multi = false){
        if(empty($data)){
            return array();
        }
     
        $tplData = json_decode($tplContent,true);
        if(!$tplData){
            return false;
        }

        $array = array();
        if (!$multi) {
            // 一维数组
            $array = self::array_intersect_key_recursive($data,$tplData);
        } else {
            // 多维数组
            foreach($data as $key => $value){
                $array[$key] = self::array_intersect_key_recursive($value,$tplData);
            }
        }

        return json_encode($array);
    }


    private static function array_intersect_key_recursive(array $array1, array $array2) {
    if(self::is_indexed_array($array1)){//如果array1为索引数组,说明有多个元素,需要把array补充为相同数目
        $diff = count($array1)-count($array2);
        for($i=0;$i<$diff;$i++){
            array_push($array2,$array2[0]);
        }
    }
        $array1 = array_intersect_key($array1, $array2);


    foreach ($array1 as $key => &$value) {
        if (is_array($value) && is_array($array2[$key])) {
        $value = self::array_intersect_key_recursive($value, $array2[$key]);
        }
    }
    return $array1;
    }


        /**
     * 判断数组是否为索引数组
    */
        private static function is_indexed_array($arr){
    if (is_array($arr)) {
        return count(array_filter(array_keys($arr), 'is_string')) === 0;
    }
        return false;
    }
}

测试代码

测试代码如下:

$tplContent = <<<EOT
{
  "name": "",
  "avatar": ""
}
EOT;

//单条数据
$user  = array('id'=>1,'name' => 'david','pass' => '123456','phone'=>'13888888888','avatar' => 'http://spetacular.github.io/images/favicon.ico');

echo JSONOut::toJSON($user,$tplContent,false);

echo "\n";

//多条数据
$users  = [array('id'=>1,'name' => 'david','pass' => '123456','phone'=>'13888888888','avatar' => 'http://spetacular.github.io/images/favicon.ico'),
array('id'=>2,'name' => 'john','pass' => '654321','phone'=>'13888888889','avatar' => 'http://spetacular.github.io/images/favicon.png')];

echo JSONOut::toJSON($users,$tplContent,true);

测试结果如下:

单条数据:

{
  "name": "david",
  "avatar": "http://spetacular.github.io/images/favicon.ico"
}

多条数据:

[
  {
    "name": "david",
    "avatar": "http://spetacular.github.io/images/favicon.ico"
  },
  {
    "name": "john",
    "avatar": "http://spetacular.github.io/images/favicon.png"
  }
]

未来扩展

注意到JSON模板仅有key,没有value,未来可以在value上扩展,比如默认值,比如:

{"gender":0}

如果数据里没有gender的数据,则默认为0;

或者在value上加过滤函数,比如:

{"age":"int"}

表示age要转化为整型数字。

Restful API中的JSON模板解析-JSONOut库的介绍

https://blog.weixinbook.net/2016/08/01/json-template-to-json.html

作者

David

发布于

2016-08-01

更新于

2023-10-22

许可协议

评论

:D 一言句子获取中...