# 第5-4课:JSON数据交换 ## 一、本课目标 完成本课后,你能够: 1. 说明JSON是什么以及为什么Web接口经常使用JSON; 2. 区分Python对象、JSON文本和网络中的字节; 3. 掌握JSON的六类基本值; 4. 使用`json.dumps()`完成序列化; 5. 使用`json.loads()`完成反序列化; 6. 识别常见JSON格式错误和不支持的数据类型; 7. 看懂HTTP请求体或响应体中的JSON。 ## 二、前置知识 - Python字典、列表、字符串、数字、布尔值和`None`; - 已理解HTTP请求体、响应体和`Content-Type`; - 本课不重复讲解Python容器和异常处理语法。 ## 三、为什么需要数据交换格式 客户端和服务器可能使用不同语言。例如浏览器使用JavaScript,后端使用Python。Python字典只能直接存在于Python程序内,不能原样作为跨语言约定。 双方需要一种共同格式: ```text Python字典 → JSON文本 → HTTP传输 → JSON文本 → JavaScript对象 ``` JavaScript对象表示法(JavaScript Object Notation,JSON)是一种轻量的文本数据格式。它由字符组成,与具体编程语言无关,因此非常适合Web接口交换结构化数据。 ## 四、不要混淆三个层次 下面三项表示相近内容,但类型不同: ```python # Python字典:程序可以按键读取和修改 book = {"id": 1, "available": True} # JSON文本:本质是一个Python字符串 json_text = '{"id": 1, "available": true}' # UTF-8字节:真正写入网络连接的数据形式之一 json_bytes = json_text.encode("utf-8") ``` 特别注意: - Python使用`True`和`None`; - JSON使用`true`和`null`; - Python字典显示时可能使用单引号; - JSON属性名和字符串必须使用双引号。 因此,`str(book)`不是可靠的JSON生成方式。 ## 五、JSON支持哪些值 | JSON类型 | JSON示例 | 转成Python后的类型 | |---|---|---| | object | `{"title": "Python"}` | `dict` | | array | `["Python", "Web"]` | `list` | | string | `"Python"` | `str` | | number | `59.9` | `int`或`float` | | boolean | `true`、`false` | `True`、`False` | | null | `null` | `None` | JSON没有元组、集合、日期时间、`Decimal`和自定义类等专用类型。发送这些值之前,需要先决定明确的表示形式。 例如日期时间通常转换成字符串: ```json {"created_at": "2026-08-20T10:30:00+08:00"} ``` 这只是常见约定,客户端和服务器仍需在接口文档中统一格式与时区。 ## 六、阅读一段JSON ```json { "id": 1, "title": "Python Web基础", "price": 59.9, "available": true, "tags": ["Python", "Web"], "description": null } ``` 从外向内阅读: 1. 最外层花括号表示一个object; 2. object中包含六组“属性名和值”; 3. `tags`的值是array; 4. array中有两个string; 5. `description`当前没有值,因此使用`null`。 JSON不允许在最后一个属性后留下多余逗号,也不支持Python风格的`# 注释`。 ## 七、序列化 序列化(Serialization)是把程序中的对象转换成便于保存或传输的格式。本课是把Python对象转换为JSON文本: ```python json_text = json.dumps(book, ensure_ascii=False, indent=2) ``` 参数含义: - `book`:需要转换的Python对象; - `ensure_ascii=False`:中文直接保留,不显示成Unicode转义; - `indent=2`:每层缩进两个空格,便于人阅读。 格式化缩进会增加数据大小,生产接口是否缩进由项目性能和可读性要求决定。 ## 八、反序列化 反序列化(Deserialization)是把JSON文本还原为Python对象: ```python request_body = '{"title": "HTTP实践", "price": 49.0}' request_data = json.loads(request_body) print(request_data["title"]) ``` `json.loads()`中的字母`s`可以帮助记忆为“读取字符串”。解析成功只说明JSON语法正确,不表示字段满足业务规则。例如`{"title": ""}`是合法JSON,但空书名可能不符合业务要求。 ## 九、JSON与HTTP怎样配合 客户端提交JSON时,应声明请求体格式: ```http POST /books HTTP/1.1 Content-Type: application/json; charset=utf-8 {"title": "Python入门"} ``` 服务器返回JSON时,也应声明响应体格式: ```http HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 {"id": 1, "title": "Python入门"} ``` `Content-Type`告诉接收方应按照什么格式解释正文。如果正文是JSON却声明为`text/plain`,有些工具仍能显示,但自动处理、文档和客户端代码可能出现问题。 ## 十、完整示例 标准示例位于`json_rest_example.py`。文件名保留了创建阶段的名称,但本课示例只负责JSON转换,不讲RESTful API;RESTful将在下一课独立学习。 示例包含: - 一个包含六种常见值的图书字典; - Python对象转JSON文本; - 保留中文并格式化缩进; - JSON请求体转Python字典; - 从解析结果中读取书名。 ## 十一、运行方法 ```powershell cd D:\Code\Python\05_web基础\5_4_JSON数据交换 python json_rest_example.py ``` ## 十二、预期结果 ```text JSON响应: { "id": 1, "title": "Python Web基础", "price": 59.9, "available": true, "tags": [ "Python", "Web" ], "description": null } 请求中的书名:HTTP实践 ``` 如果看到`true`和`null`,说明输出是JSON写法,不是Python字典的直接显示结果。 ## 十三、代码执行顺序 1. Python导入标准库`json`; 2. 程序从入口调用`main()`; 3. `serialize_book()`创建图书字典; 4. `json.dumps()`返回JSON字符串; 5. `print()`把JSON字符串显示到终端; 6. `deserialize_request()`接收一段JSON请求体; 7. `json.loads()`返回Python字典; 8. 程序读取字典中的`title`并输出。 ## 十四、动手观察 建议复制示例后依次做以下小实验,每次只改一处: 1. 删除`ensure_ascii=False`,观察中文显示方式; 2. 把`indent=2`改为`indent=4`,观察缩进; 3. 在字典中增加`"stock": 10`,观察number; 4. 把请求体中的双引号改成单引号,观察解析错误; 5. 在JSON最后一个属性后增加逗号,观察解析错误。 实验结束后恢复标准示例,不要把故意制造的错误保留在示例文件中。 ## 十五、常见错误 ### 15.1 把Python字典文本当成JSON `{'available': True}`是Python显示形式,不是合法JSON。应使用`json.dumps()`生成。 ### 15.2 JSON使用单引号 JSON属性名和字符串必须使用双引号。接口工具中输入单引号通常会导致400错误。 ### 15.3 最后一项后保留逗号 JSON标准不允许尾随逗号。编辑器中的JavaScript对象可能允许,但JSON解析器通常拒绝。 ### 15.4 直接序列化不支持的对象 `Decimal`、日期时间和ORM对象不能默认转换。应先转换为接口明确约定的字符串、数字或字典。 ### 15.5 解析成功就直接写数据库 JSON语法正确不等于业务有效。服务器还要校验必填字段、长度、数值范围和权限。 ### 15.6 把敏感字段放入响应 对象能序列化不代表所有字段都应返回。密码、密钥和内部状态必须排除。 ## 十六、课堂练习 打开`practice.py`,依次完成基本转换、错误处理和HTTP消息观察。练习文件不包含答案。 ## 十七、本课小结 JSON是文本格式,不是Python字典。`json.dumps()`负责序列化,`json.loads()`负责反序列化。HTTP通过`Content-Type`声明正文是JSON,语法解析之后仍要进行业务校验。 ## 十八、验收标准 - 能区分Python对象、JSON文本和UTF-8字节; - 能说出JSON支持的六类值; - 能独立完成序列化和反序列化; - 能解释`ensure_ascii`和`indent`的作用; - 能识别单引号、尾随逗号等格式错误; - 能说明JSON语法校验与业务字段校验的区别; - 示例可独立运行; - 完成`practice.py`中的练习。