深度:apiDoc使用解析及整合格式存放Elasticsearch

前言

apiDoc是我们在日常开发中经常会使用到的一款开源的api文档解析框架,使用apiDoc可以轻松的将我们代码中的api解析成为可以方便阅读的api文档,只需轻松的按照格式进行编辑即可,使用起来非常方便。但是,apiDoc也存在一定的问题,即apiDoc解析生成的文档,通常格式上都不是特别严谨,这样它就无法存放在一些NoSQL的框架如Elasticsearch中,因此,本文后面提供了一种解决思路,将apiDoc生成的文档,进行标准化成为统一的格式,可存放到Elasticsearch中,方便我们后期再做管理。

入门

首先是apiDoc的入门教程。

安装&运行

安装:
npm install apidoc -g
运行:
apidoc -i myapp/ -o apidoc/ -t mytemplate/

-i 指定输入的源代码文件,-o 指定输出的文件夹,-t 指定模板文件的位置。

如何编写

apiDoc支持多种语言,包括但不限于:C#, Go, Dart, Java, JavaScript, PHP, TypeScript, CoffeeScript, Elixir, Erlang, Perl, Python, Ruby, Lua等。
而如何编写呢,就是在不同语言的多行注释内,比如Java:

/**
 * @api {get} /user/:id Request User information
 * @apiName GetUser
 * @apiGroup User
 *
 * @apiParam {Number} id Users unique ID.
 *
 * @apiSuccess {String} firstname Firstname of the User.
 * @apiSuccess {String} lastname  Lastname of the User.
 */

还有python:

"""
@api {get} /user/:id Request User information
@apiName GetUser
@apiGroup User

@apiParam {Number} id Users unique ID.

@apiSuccess {String} firstname Firstname of the User.
@apiSuccess {String} lastname  Lastname of the User.
"""

怎么样,看上面的例子,是不是非常简单呢。
具体如何使用,查查官方文档即知:apiDoc官方

解析

apiDoc可以读取我们编写的代码,并将其转化为apiDoc的文档,当我们打开文件夹时,会发现下面有两个文件,一个是api_project.js,另一个是api_data.js,没错,这就是apiDoc的数据了。其中api_project是项目的数据,这部分的数据主要描述的是我们项目文档的整体介绍,而api_data.js则是具体的api数据了。因此,当我们看到用apiDoc生成的文档时,我们也可以通过curl或其他方式,下载这两个文件,轻松获得api文档的所有数据。

转换

具体查看apiDoc生成的数据,我们就会发现,这两个文件生成的,里面的数据并非完全一致的,有些字段并不是每个项都有,这就非常不好了,不适合Elasticsearch内数据的存放使用,因此我们需要对这些数据进行一定的转化,转化为我们需要的标准Json数据。
以下使用python构建,python内的json不是特别符合我们的需求,因此我们专门定制了一个Json类,对这个类有如下要求:
1. 自动处理str类型、dict类型、list类型、None(null)、布尔类型等
2. 格式化输出json,且保证不浪费任何一个字节
3. 处理str类型时需要注意换行符,双引号等问题

为了解决第1个和第3个问题,我们编写了如下代码:
其中按照不同的类型进行了相应的处理,并利用了递归函数的技巧,来使得程序代码更加简化。

    def _json_string(self, value):
        if value != None:
            if isinstance(value, str):
                return "\"" + value.replace("\"", "\\\"").replace("\n", "\\n") + "\""
            elif isinstance(value, dict):
                dict_json = "{"
                isFirst = True
                for key in iter(value):
                    if not isFirst:
                        dict_json += ","
                    isFirst = False
                    dict_json += "\"" + key + "\":" + self._json_string(value[key])
                return dict_json + "}"
            elif isinstance(value, list):
                sub_list = "["
                isFirst = True
                for d in value:
                    if not isFirst:
                        sub_list += ","
                    isFirst = False
                    sub_list += self._json_string(d)
                return sub_list + "]"
            elif isinstance(value, bool):
                if value:
                    return "true"
                return "false"
            else:
                return str(value)
        return "null"

接下来我们只需要简单的输出即可:

    def __str__(self):
        string = ""
        isFirst = True
        for key in iter(self._data):
            if not isFirst:
                string += ","
            isFirst = False
            string += "\"" + str(key) + "\":"
            string += self._data[key]
        return "{" + string + "}"

上面的这个Json类是可以复用的。
下面来针对apiDoc编写专门的转换代码:
首先是解决对象内是否含有该键的问题:

def _valid_key(api, key):
    if api != None and key != None \
            and api.keys().__contains__(key):
        return api[key]
    return None

然后是解决共有的数据问题,即api_project.js的数据:
因为我们的数据是直接从网络上下载的,所有需要去除前面的无用信息,然后根据api_project的格式,我们提取出几个有效信息即name、title、description、version、header下的content字段,生成了Json对象返回。

def _parse_header(header_data):
    header_data = header_data[7:len(header_data)-3]
    json_data = json.loads(header_data.decode('utf-8'))
    keys = ['name', 'title', 'description', 'version']
    data = Json()
    for key in keys:
        data.append(key, _valid_key(json_data, key))
    data.append("content", _valid_key(_valid_key(json_data, 'header'), 'content'))
    return data

接下来解析api_data.js内的数据:

def _parse_api(data):
    data = data[7:len(data)-3]
    json_data = json.loads(data.decode('utf-8'))
    apis = json_data['api']
    simple_keys = ["type", "url", "title", "group", "groupTitle",
                    "description", "examples", "version"]
    dup_keys = ["parameter", "error", "success"]
    api_data_list = []
    for api in apis:
        data = Json()
        for key in simple_keys:
            data.append(key, _valid_key(api, key))
        for key in dup_keys:
            if _valid_key(api, key) == None:
                data.append(key, None)
            else:
                sub_json = Json()
                sub_json.append('examples', _valid_key(api[key], 'examples'))
                info = []
                if _valid_key(api[key], 'fields') != None:
                    for v in _valid_key(api[key], 'fields').values():
                        info.append(v)
                sub_json.append('info', info)
                data.append(key, sub_json)
        api_data_list.append(data)
    return api_data_list

这个函数相对而言比较复杂,但也就是针对数据进行定制化罢了。

附录

完整代码贴在下方,以供参考。

my_json.py 代码

#!/usr/bin/env python
# coding=utf-8

class Json:

    def __init__(self):
        self._data = dict()

    def _json_string(self, value):
        if value != None:
            if isinstance(value, str):
                return "\"" + value.replace("\"", "\\\"").replace("\n", "\\n") + "\""
            elif isinstance(value, dict):
                dict_json = "{"
                isFirst = True
                for key in iter(value):
                    if not isFirst:
                        dict_json += ","
                    isFirst = False
                    dict_json += "\"" + key + "\":" + self._json_string(value[key])
                return dict_json + "}"
            elif isinstance(value, list):
                sub_list = "["
                isFirst = True
                for d in value:
                    if not isFirst:
                        sub_list += ","
                    isFirst = False
                    sub_list += self._json_string(d)
                return sub_list + "]"
            elif isinstance(value, bool):
                if value:
                    return "true"
                return "false"
            else:
                return str(value)
        return "null"

    def append(self, key, value=None):
        if key == None:
            return
        self._data[key] = self._json_string(value)

    def __str__(self):
        string = ""
        isFirst = True
        for key in iter(self._data):
            if not isFirst:
                string += ","
            isFirst = False
            string += "\"" + str(key) + "\":"
            string += self._data[key]
        return "{" + string + "}"

apidoc_parse.py

#!/usr/bin/env python
# coding=utf-8
import urllib.request
import json
from my_json import Json

def _valid_key(api, key):
    if api != None and key != None \
            and api.keys().__contains__(key):
        return api[key]
    return None

def _parse_api(data):
    data = data[7:len(data)-3]
    json_data = json.loads(data.decode('utf-8'))
    apis = json_data['api']
    simple_keys = ["type", "url", "title", "group", "groupTitle",
                    "description", "examples", "version"]
    dup_keys = ["parameter", "error", "success"]
    api_data_list = []
    for api in apis:
        data = Json()
        for key in simple_keys:
            data.append(key, _valid_key(api, key))
        for key in dup_keys:
            if _valid_key(api, key) == None:
                data.append(key, None)
            else:
                sub_json = Json()
                sub_json.append('examples', _valid_key(api[key], 'examples'))
                info = []
                if _valid_key(api[key], 'fields') != None:
                    for v in _valid_key(api[key], 'fields').values():
                        info.append(v)
                sub_json.append('info', info)
                data.append(key, sub_json)
        api_data_list.append(data)
    return api_data_list

def _parse_header(header_data):
    header_data = header_data[7:len(header_data)-3]
    json_data = json.loads(header_data.decode('utf-8'))
    keys = ['name', 'title', 'description', 'version']
    data = Json()
    for key in keys:
        data.append(key, _valid_key(json_data, key))
    data.append("content", _valid_key(_valid_key(json_data, 'header'), 'content'))
    return data

# 输入apidoc生成的模板数据,生成统一的模板数据
def model_data(header_data, data):
    api_data = _parse_header(header_data)
    api_data.append('api', _parse_api(data))
    return str(api_data)

if __name__ == '__main__':
    header_data = urllib.request.urlopen('http://localhost:5000/static/api_project.js')
    data = urllib.request.urlopen('http://localhost:5000/static/api_data.js')
    model = model_data(header_data.read(), data.read())
    print(model)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值