json 中集合的最佳实践:数组与 dict/map

lev*_*eer 2 python json

我需要使用 json 格式通过 api 调用将 python 后端中的数据传递到前端。在python后端,数据是字典结构,我可以轻松直接将其转换为json。但我应该吗?

我的前端开发人员认为答案是否定的,原因与最佳实践相关。

但我对此提出质疑:

最好像在 python 中一样构建 json,还是应该将其转换为其他形式,例如几个数组(如下面我的示例案例中所必需的)?

或者,换句话说:

通过 json 连接信息的集合/字典/映射/数组相关的管理原则应该是什么?

我已经用谷歌搜索了一些答案,但我没有找到太多直接解决这个问题的答案。链接将不胜感激。

(请注意下面的示例:当然,如果数据写入数据库,前端直接访问数据库可能是最有意义的,但我们假设情况并非如此)

例子:

在后端有一个名为 的对象集合pets:集合中的每个项目都有一个唯一的pet_id、一些非可选属性,例如namedate_of_birth、一些可选属性 registration_certificate_nradopted_from_kennel、一些列表(如 )siblingschildren一些对象(如 )medication

假设前端在某个时候需要所有这些信息,它可能是

{
  "pets": {
    "17-01-24-01": {
      "name": "Buster",
      "date_of_birth": "04/01/2017",
      "registration_certificate_nr": "AAD-1123-1432"
    },
    "17-03-04-01": {
      "name": "Hooch",
      "date_of_birth": "05/02/2015",
      "adopted_from_kennel": "Pretoria Shire",
      "children": [
        "17-05-01-01",
        "17-05-01-02",
        "17-05-01-03"
      ]
    },
    "17-05-01-01": {
      "name": "Snappy",
      "date_of_birth": "17-05-01",
      "siblings": [
        "17-05-01-02",
        "17-05-01-03"
      ]
    },
    "17-05-01-02": {
      "name": "Gizmo",
      "date_of_birth": "17-05-01",
      "siblings": [
        "17-05-01-01",
        "17-05-01-03"
      ]
    },
    "17-05-01-03": {
      "name": "Toothless",
      "date_of_birth": "17-05-01",
      "siblings": [
        "17-05-01-01",
        "17-05-01-03"
      ],
      "medication": [
        {
          "name": "anti-worm",
          "code": "aw445",
          "dosage": "1 pill per day"
        },
        {
          "name": "disinfectant",
          "code": "pdi-2",
          "dosage": "as required"
        }
      ]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Coe*_*aar 7

JSON 格式化是一个有点主观的问题,相关的分歧通常最好在同事之间解决。
话虽这么说,但问题中的 JSON 格式可能存在一些有效的批评,特别是当我们尝试创建一致的 RESTful API 时。

2个突出的痛点:

  1. 地图集合以 JSON 表示,这并不真正符合 JSON 标准,或者特别不符合 RESTful 标准。

  2. 所有宠物对象都没有id定义。pet_id问题中提到了一个,但它似乎是与宠物对象本身分开维护的。如果在问题中的地图中访问某个值pets,则 API 用户必须手动将其添加pet_id到提供的 pet 对象中,以便在完整的 JSON 可能不再可用时使 id 进一步可用。

在这种情况下,我们最接近指导标准的是REST 架构风格JSON 标准


我们可以从查看 JSON 标准开始。以下是来自JSON wiki的引用:

JavaScript 语法定义了 JSON 标准中未包含的几种本机数据类型:Map、Set、Date、Error、正则表达式、Function、Promise 和undefined.

这里的关键要点是 JSON 并不意味着表示地图数据类型。Python 字典是一种映射实现,因此直接将字典序列化为 JSON 来表示类似映射的集合与 JSON 的预期用途背道而驰。

对于像宠物这样的单个对象,JSON 对象是合适的,但对于集合,有一种选择:JSON 数组。在这个答案的下面有一个使用 JSON 数组的示例。

可能存在一些边缘情况,偏离标准是有意义的,但我在这种情况下看不到任何原因。


从 RESTful 设计的角度来看,JSON 格式也存在一些缺点。RESTful API 设计很好,因为它鼓励人们保持简单和一致。它也恰好是事实上的行业标准。

在 RESTful HTTP API 中,获取单个宠物资源应如下所示:

Request: GET /api/pets/17-01-24-01

Response: 200 {
    "id": "17-01-24-01",
    "name": "Buster",
    "date_of_birth": "04/01/2017",
    "registration_certificate_nr": "AAD-1123-1432"
}
Run Code Online (Sandbox Code Playgroud)

响应是一个完全定义的资源,具有显式定义的id. 它也是宠物最简单的完整 JSON 表示形式。

接下来,我们定义获取多个宠物资源的方式,假设只定义了 2 个宠物:

Request: GET /api/pets

Response: 200 [
    {
        "id": "17-01-24-01",
        "name": "Buster",
        "date_of_birth": "04/01/2017",
        "registration_certificate_nr": "AAD-1123-1432"
    },
    {
        "id": "17-03-04-01",
        "name": "Hooch",
        "date_of_birth": "05/02/2015",
        "adopted_from_kennel": "Pretoria Shire",
        "children": [
            "17-05-01-01",
            "17-05-01-02",
            "17-05-01-03"
         ]
    }
]
Run Code Online (Sandbox Code Playgroud)

上述响应格式是将单一资源响应格式复数化的最直接的方式,从而使 API 尽可能简单和一致。(为了简洁起见,我只使用了问题中的 2 个示例资源)。再次强调,ids 是明确定义的,并且属于它们各自的 pet 对象。

将地图键添加到上述格式不会获得任何好处。

问题中 JSON 格式的支持者可能建议仅将字段添加id到每个宠物对象中,以便解决痛点 2,但这会引发响应中重复数据的问题。为什么id需要既在对象内部又在对象外部?当然它应该只在内部?消除冗余数据后,结果将类似于上面的响应。

这就是 REST 论点。在某些用例中,REST 并不能真正发挥作用,但事实远非如此。


附言。前端永远不应该直接访问数据库。API 负责写入和读取所使用的任何数据持久性基础设施。在许多更大的现实系统中,前端和 API 之间甚至还有一个额外的 BFF 层,进一步将前端和数据库分开。

  • 写得好 - 谢谢你。支持使用数组的其他要点 - (1) 随着宠物列表变大,您可能需要添加分页和其他此类过滤,这在数组中更有意义。(2) 迭代对象/映射可能是变化无常的 - 各种 js 框架可能会以不同的顺序迭代对象,从而产生意外的行为(这取自 Vue v-for 文档) (3认同)