Skip to content

How to make another APIJSON? / 如何实现其它语言的APIJSON? #38

Open

Description

@TommyLemon

目前已有 C#, Go, Java, JavaScript, Node, PHP, Python, Rust, TypeScript 对应的 APIJSON ORM 库实现
https://github.com/Tencent/APIJSON#%E7%94%9F%E6%80%81%E9%A1%B9%E7%9B%AE

一开始不要贪大求全,实现基础的功能就好,功能后面再完善、性能后面再优化。

可以参考Java版的实现,建议从这个版本做起
https://github.com/TommyLemon/APIJSON/tree/636fe2263c01acce4ad32751cbb8a579830d9f29

这个是最简单的实现,只有7个类,将JSON转换为SQL,但也能够实现最核心的 引用赋值、单表查询、数组分页查询 了。
先完成这个,后面再优化。如果直接实现复杂功能,很可能导致一大堆问题然后坚持不下去。

Activity

  1. TommyLemon commented on Jun 20, 2018

    @TommyLemon
    MemberAuthor

    image

    建议步骤:

    1.实现单表查询

    {
        "User": {
            "id": 82002
        }
    }

    转换为

    SELECT * FROM User WHERE id = 82002 LIMIT 1 OFFSET 0

    ResultSet取第0条(总数最多1条),取出所有字段对应的值,封装为一个表对象返回:

    {
        "id": 82002,
        "sex": 1,
        "name": "Happy~",
        "tag": "iOS",
        "head": "http://static.oschina.net/uploads/user/1174/2348263_50.png?t=1439773471000",
        "contactIdList": [
            82005,
            82001,
            38710
        ],
        "pictureList": [],
        "date": "2017-02-01 19:21:50.0"
    }

    替换原来的

    {
        "id": 82002
    }

    2.实现数组查询

    {
        "[]": {
            "count": 10,
            "page": 1,
            "User": {
                "sex": 1
            }
        }
    }

    转换为

    SELECT * FROM User WHERE sex = 1 LIMIT 10 OFFSET 10

    ResultSet取所有,封装为一个包含表对象的数组返回:

    [
        {
            "User": {
                "id": 82005,
                "sex": 1,
                "name": "Jan",
                "tag": "AG",
                "head": "http://my.oschina.net/img/portrait.gif?t=1451961935000",
                "contactIdList": [
                    82001,
                    38710
                ],
                "pictureList": [],
                "date": "2017-02-01 19:21:50.0"
            }
        },
        {
            "User": {
                "id": 82006,
                "sex": 1,
                "name": "Meria",
                "head": "http://static.oschina.net/uploads/user/998/1997902_50.jpg?t=1407806577000",
                "contactIdList": [],
                "pictureList": [],
                "date": "2017-02-01 19:21:50.0"
            }
        }
    ]

    替换原来的

    {
        "count": 10,
        "page": 1,
        "User": {
            "sex": 1
        }
    }

    3.实现两张表 一对一 关联查询

    {
        "Comment": {},
        "User": {
            "id@": "Comment/userId"
        }
    }

    先解析Comment,转换为

    SELECT * FROM Comment LIMIT 1 OFFSET 0

    ResultSet取第0条(总数最多1条),取出所有字段对应的值,封装为一个Comment表对象返回:

    {
        "id": 4,
        "toId": 0,
        "userId": 38710,
        "momentId": 470,
        "date": "2017-02-01 19:20:50.0",
        "content": "This is a Content...-4"
    }

    然后解析User,解析到

    "id@": "Comment/userId"

    时,从Comment中取出userId,把以上键值对改为

    "id": 38710
    

    然后把User转换为

    SELECT * FROM User WHERE id = 38710 LIMIT 1 OFFSET 0

    ResultSet取第0条(总数最多1条),取出所有字段对应的值,封装为一个User表对象返回:

    {
        "id": 38710,
        "sex": 0,
        "name": "TommyLemon",
        "tag": "Android&Java",
        "head": "http://static.oschina.net/uploads/user/1218/2437072_100.jpg?t=1461076033000",
        "contactIdList": [
            82003,
            82005,
            70793
        ],
        "pictureList": [
            "http://static.oschina.net/uploads/user/1218/2437072_100.jpg?t=1461076033000",
            "http://common.cnblogs.com/images/icon_weibo_24.png"
        ],
        "date": "2017-02-01 19:21:50.0"
    }

    4.实现两张表 一对多 关联查询

    {
        "Moment": {},
        "[]": {
            "Comment": {
                "momentId@": "Moment/id"
            }
        }
    }

    5.实现两张表在数组内 一对一 关联查询

    {
        "[]": {
            "Comment": {},
            "User": {
                "id@": "[]/Comment/userId"
            }
        }
    }

    其中

    "id@": "[]/Comment/userId"

    要根据Comment在数组中的位置index来动态变为

    "id@": "[]/0/Comment/userId"
    "id@": "[]/1/Comment/userId"

    ...

    6.实现两张表在数组内 一对多 关联查询

    实现了 1-6 后可以测试是否能通过这个用例(一对一、一对多、数组外、数组内、同层级、跨层级都有)
    963D6EF9B87189AA9AD5FEEE84CC3149

    http://apijson.cn/api

    POST http://apijson.cn:8080/get

    {
        "Moment": {
            "id": 32,
            "@column": "id,userId,praiseUserIdList"
        },
        "User": {
            "id@": "Moment/userId",
            "@column": "id"
        },
        "[]": {
            "Moment": {
                "userId{}@": "Moment/praiseUserIdList",
                "@column": "id,userId"
            },
            "User": {
                "id@": "/Moment/userId",
                "@column": "id,name,head"
            },
            "Comment[]": {
                "Comment": {
                    "momentId@": "[]/Moment/id"
                }
            }
        }
    }

    7.实现SQL的 column, order by, group by等功能。

    8.实现增、删、改

    APIAuto 上有 200 左右测试用例,可以跑一遍看看哪些有问题
    http://apijson.cn/api

    管理员账号及密码 13000082001/123456
    image

    管理员账号及密码 13000088888/123456
    image

    测试数据 SQL 文件(MySQL, PostgreSQL, Oracle, SQL Server, DB2, ClickHouse 等)
    https://github.com/APIJSON/APIJSON-Demo

    完整功能见设计规范(核心的任意多表关联查询功能完成后可逐步完善其它功能)
    https://github.com/Tencent/APIJSON/blob/master/Document.md#3

  2. TommyLemon commented on Jun 20, 2018

    @TommyLemon
    MemberAuthor

    由于不限制嵌套层级和组合方式,所以必须用递归实现,从数据库查到结果(一开始可模拟测试数据,省去连接数据库的过程)后替换掉原来的内容再返回。

    递归时由于不知道键值对key:value中的key和value,所以需要判断key和value的格式。

    {
        "[]": { // value 类型为 JSONObject  &&  key 以 [] 结束
            "User": { // value 类型为 JSONObject  &&  key 符合正则表达式  ^[A-Za-z]+$
                "sex": 1 // value 类型不为 JSONObject
            }
        }
    }
  3. TommyLemon commented on Jun 20, 2018

    @TommyLemon
    MemberAuthor

    最简单的解析过程可以是:

    1.Parser递归遍历 请求JSON,提取出每个 表对象JSON
    2.Parser用 表对象JSON 封装SQLConfig,交给SQLExecutor
    3.SQLExecutor用SQLConfig封装SQL语句,并连接数据库执行
    4.SQLExecutor返回执行结果给Parser
    5.Parser替换对应请求JSON中的对象

  4. TommyLemon commented on Jul 4, 2018

    @TommyLemon
    MemberAuthor

    APIJSON-C# Server:
    创作不易,给热心的作者右上角点 ⭐Star 支持下吧 ^_^
    https://github.com/liaozb/APIJSON.NET

  5. zeromake commented on Aug 9, 2018

    @zeromake

    @TommyLemon

    上回说的python初步完成了 https://github.com/zeromake/restful_model 刚刚把单元测试写好。
    json 的表现层完全自定义,都是为了对应 sql。

    import sqlalchemy as sa
    from sanic import Sanic
    from restful_model import DataBase
    from restful_model.extend.sanic import ApiView
    
    metadata = sa.MetaData()
    # 这个model可以直接用工具连接数据库生成
    User = sa.Table(
        'user',
        metadata,
        sa.Column(
            'id',
            sa.Integer,
            autoincrement=True,
            primary_key=True,
            nullable=False,
        ),
        sa.Column(
            'account',
            sa.String(16),
            nullable=False,
        ),
        sqlite_autoincrement=True,
    )
    # python 可以动态生成 class 后面想批量注册model到view也很简单
    class UserView(ApiView):
        __model__ = User
        # 请求方法过滤
        # __method__ = ["get", "post"]
        # 过滤 where 或者查询字段,支持全局或单个方法过滤
        #__filter_keys__ = {"get": [["id"]]}
        # 中间件模式
        async def auth_filter(self, context: Context, next_filter):
            return await next_filter()
    app = Sanic(__name__)
    db = DataBase("sqlite:///db.db")
    app.db = db
    
    @app.listener('before_server_start')
    async def setup_db(app, loop):
        if app.db.loop is None:
            app.db.loop = loop
            app.db.engine = await app.db.create_engine(echo=True)
            if not await app.db.exists_table(User.name):
                await app.db.create_table(User)
    
    userView = UserView.as_view(app.db)
    app.add_route(userView, "/user", HTTP_METHODS)
    app.add_route(userView, "/user/<id:int>", HTTP_METHODS)
    
    if __name__ == "__main__":
        app.run(host="0.0.0.0", port=8000)
    $ # create
    $ curl -X POST http://127.0.0.1:8000/user \
    -H 'content-type: application/json' \
    -d '{ "account": "test1" }'
    > {
        "status": 201,
        "message": "Insert ok!",
        "meta": {"count":1}
    }
    $ # select
    $ curl -X GET http://127.0.0.1:8000/user
    > {
        "status": 200,
        "message": "Query ok!",
        "data": [{
            "id": 1,
            "account": "test1"
        }]
    }
    $ # update
    $ curl -X PUT http://127.0.0.1:8000/user \
    -H 'content-type: application/json' \
    -d '{"where": {"id": 1}, "values": {"account": "test2"}}'
    > {
        "status": 201,
        "message": "Update ok!",
        "meta":{
            "count": 1
        }
    }
    $ curl -X GET http://127.0.0.1:8000/user
    > {
        "status": 200,
        "message": "Query ok!",
        "data": [
            {"id": 1,"account": "test2"}
        ]
    }
    $ # delete
    $ curl -X DELETE http://127.0.0.1:8000/user \
    -H 'content-type: application/json' \
    -d '{"id": 1}'
    > {
        "status": 200,
        "message": "Delete ok!",
        "meta": {"count":1}
    }
    $ curl -X GET http://127.0.0.1:8000/user
    > {
        "status": 200,
        "message": "Query ok!",
        "data": []
    }

    现在还是觉得权限管理不太给力,虽然用中间件模式,什么样的权限都做的了。

    以及多表连接在想是提供一个 class 然后把多张表设置上去,url 和权限独立,还是像你的 APIJSON 全局注册所有表随意连接任意表。
    我这个库就是一个 python 库,然后不对任意一个 web 框架强绑定。
    现在写了 Sanic, Tornado 的支持。

    后面有心情再考虑把 json 的表现层抽出来,做了 APIJSON 的 json 表现层。
    现在还有个问题就是 Date 的格式化函数 sqlite, mysql, pg 每个都不一样,现在想要么都不用,全用 python 的格式..

  6. TommyLemon commented on Aug 9, 2018

    @TommyLemon
    MemberAuthor

    @zeromake 赞,已Star。
    不过URL里不要加user等符号哦,应该放到请求JSON里面,方便传任意表名

    {
        "User": {
            "id": 70793
        }
    }
    {
        "Moment": {
            "id": 12
        }
    }

    ...
    还能自由组合

    {
        "Moment": {
            "id": 12
        },
        "User": {
            "id": 70793
        }
    }

    不知道User.id的情况下可以通过 引用赋值 得到

    {
        "Moment": {
            "id": 12
        },
        "User": {
            "id@": "Moment/userId" // User.id = Moment.userId
        }
    }
    {
        "Moment": {
            "id": 12
        },
        "[]": {
            "User": {
                "id{}@": "Moment/praiseUserIdList" // json_contains(Moment.praiseUserIdList, User.id)
            }
        }
    }

    参考上面的 建议步骤 和 设计规范

  7. TommyLemon commented on Aug 9, 2018

    @TommyLemon
    MemberAuthor

    @zeromake 你改好后我就在APIJSON主页加上这个项目的链接哈,和 C#版 一样
    https://github.com/TommyLemon/APIJSON

  8. zeromake commented on Aug 9, 2018

    @zeromake

    @TommyLemon 我这个只是为了做基础控件的,对应某个表操作,全局可连接任意表的应当后面做一个单独的控制 view,全部由使用的人来选择是注册一个全局 url 进行控制,还是挂几个表view。后面都会做对应的包括单表和 APIJSON 的相同的全局可连接任意表。

  9. TommyLemon commented on Aug 9, 2018

    @TommyLemon
    MemberAuthor

    @zeromake 这样啊,非常期待,记得通知我哦,有问题欢迎找我交流

  10. zeromake commented on Aug 10, 2018

    @zeromake

    @TommyLemon
    issues交流比较麻烦,base64解密防搜索引擎
    加我 wx:YWZseTM5MA== ;或者qq:MzkwNzIwMDQ2

  11. TommyLemon commented on Aug 10, 2018

    @TommyLemon
    MemberAuthor

    @zeromake 可以

  12. TommyLemon commented on Sep 27, 2018

    @TommyLemon
    MemberAuthor

    APIJSON Node.js 版,基于 typeorm ,使用 TypeScript 实现。

    创作不易,给热心的作者右上角点 ⭐Star 支持下吧 ^_^
    https://github.com/TEsTsLA/apijson

  13. yuu2lee4 commented on Sep 27, 2018

    @yuu2lee4
  14. TommyLemon commented on Oct 22, 2018

    @TommyLemon
    MemberAuthor

    已经有热心的开发者实现了 PHP 版的 APIJSON 🎉
    创作不易,给作者点 ⭐Star 支持下吧^_^
    https://github.com/orchie/apijson

  15. 5 remaining items

  16. pinned this issue on Jan 25, 2019
  17. self-assigned this
    on Mar 2, 2019
  18. TommyLemon commented on Mar 30, 2019

    @TommyLemon
    MemberAuthor

    @never615
    另一个 PHP 版 APIJSON,看起来功能比原来那个多不少,可以试试,点 Star 支持下吧^_^
    https://github.com/qq547057827/apijson-php

  19. TommyLemon commented on Jun 4, 2019

    @TommyLemon
    MemberAuthor

    APIJSON Go 语言版 APIJSON
    https://github.com/crazytaxi824/APIJSON

  20. unpinned this issue on Jan 3, 2021
  21. TommyLemon commented on Feb 10, 2021

    @TommyLemon
    MemberAuthor

    @never615 @SwankyTigerYY @y449329998 @ben1one
    最近发现了一个新的 APIJSON PHP 版实现,代码和文档看起来比之前的都要完善一些,可以试试,能用的话就 Star 鼓励下作者~
    https://github.com/xianglong111/json-api

    image
    image
    image

  22. pinned this issue on Mar 12, 2022
  23. TommyLemon commented on Mar 13, 2022

    @TommyLemon
    MemberAuthor

    31D2FB32-85E1-42BA-863F-1A12857A0597
    注:getQuote 是获取引号,避免表名、字段名等和关键词冲突,例如 MySQL 用反引号 `name`,PostgreSQL, Oracle, SQLServer, DB2 都可以用双引号 "date"

  24. glennliao commented on Oct 27, 2022

    @glennliao
    Contributor
  25. changed the title [-]如何实现其它语言的APIJSON?[/-] [+]如何实现其它语言的APIJSON?/How to make another APIJSON?[/+] on Nov 23, 2022
  26. changed the title [-]如何实现其它语言的APIJSON?/How to make another APIJSON?[/-] [+]如何实现其它语言的APIJSON?/ How to make another APIJSON?[/+] on Nov 23, 2022
  27. changed the title [-]如何实现其它语言的APIJSON?/ How to make another APIJSON?[/-] [+]How to make another APIJSON? / 如何实现其它语言的APIJSON?[/+] on Nov 23, 2022
  28. jimisun123 commented on Aug 22, 2023

    @jimisun123

    @TommyLemon 我想问一下,生成的SQL是有基于什么标准吗? 怎么才能做到生成的SQL兼容各种数据库?

  29. TommyLemon commented on Aug 22, 2023

    @TommyLemon
    MemberAuthor

    @TommyLemon 我想问一下,生成的SQL是有基于什么标准吗? 怎么才能做到生成的SQL兼容各种数据库?

    @jimisun123
    根据配置的数据库类型和版本适配,例如 Java 版是在 DemoSQLConfig 重新父类 AbstractSQLConfig 的方法 getDatabase, getDBVersion
    优先 MySQL/PostgreSQL 等热门开源数据库的 SQL 语法和 JDBC/ODBC。

  30. TommyLemon commented on Jan 2, 2026

    @TommyLemon
    MemberAuthor

    新增 Rust 版 APIJSON - 一个优雅、高性能的多数据源管理系统,支持 MySQL, PostgreSQL 的统一管理和操作
    创作不易,右上角点亮 ⭐️ Star 支持下热心的作者吧 ^_^
    https://gitee.com/APIJSON/panda-base

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions