feat(web基础): 新增零基础Web开发课程
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
# 第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`中的练习。
|
||||
@@ -0,0 +1,36 @@
|
||||
"""第5-4课标准示例:完成Python对象与JSON文本的双向转换。"""
|
||||
|
||||
import json
|
||||
|
||||
|
||||
def serialize_book() -> str:
|
||||
"""把Python图书对象序列化为JSON文本。"""
|
||||
|
||||
book = {
|
||||
"id": 1,
|
||||
"title": "Python Web基础",
|
||||
"price": 59.9,
|
||||
"available": True,
|
||||
"tags": ["Python", "Web"],
|
||||
"description": None,
|
||||
}
|
||||
return json.dumps(book, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
def deserialize_request(request_body: str) -> dict:
|
||||
"""把客户端发送的JSON文本反序列化为Python字典。"""
|
||||
|
||||
return json.loads(request_body)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""依次展示序列化和反序列化结果。"""
|
||||
|
||||
print("JSON响应:")
|
||||
print(serialize_book())
|
||||
request_data = deserialize_request('{"title": "HTTP实践", "price": 49.0}')
|
||||
print(f"请求中的书名:{request_data['title']}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,60 @@
|
||||
# 第5-4课练习:JSON数据交换
|
||||
#
|
||||
# 本文件只提供题目、预期结果、自查清单和验收标准,不包含参考答案或代码骨架。
|
||||
|
||||
|
||||
# 第一部分:创建并序列化Python对象
|
||||
# 1. 创建一个图书字典,包含编号、书名、作者、价格、是否上架、标签和备注。
|
||||
# 2. 值应覆盖字符串、数字、布尔值、列表和None。
|
||||
# 3. 使用json.dumps()转换为JSON文本。
|
||||
# 4. 要求中文直接显示,并使用两个空格缩进。
|
||||
# 5. 输出转换后的JSON文本。
|
||||
|
||||
|
||||
# 第二部分:反序列化请求体
|
||||
# 1. 准备一段新增图书的合法JSON字符串。
|
||||
# 2. 使用json.loads()转换为Python字典。
|
||||
# 3. 分别输出书名、作者和价格。
|
||||
# 4. 输出转换后对象的类型,确认它是dict而不是str。
|
||||
|
||||
|
||||
# 第三部分:处理错误JSON
|
||||
# 1. 依次测试属性名使用单引号、最后一项带逗号、缺少右花括号三种错误。
|
||||
# 2. 捕获json.JSONDecodeError。
|
||||
# 3. 为使用者输出简洁中文提示,不直接把程序终止在英文堆栈处。
|
||||
# 4. 在注释中记录每段JSON为什么无效。
|
||||
|
||||
|
||||
# 第四部分:观察HTTP中的JSON
|
||||
# 1. 用多行字符串写出一份POST /books请求报文。
|
||||
# 2. 请求头必须包含Content-Type: application/json; charset=utf-8。
|
||||
# 3. 请求体包含title与price。
|
||||
# 4. 再写出一份状态码为201的JSON响应报文。
|
||||
# 5. 在注释中标出请求体、响应体与Content-Type分别位于哪里。
|
||||
|
||||
|
||||
# 预期关键输出:
|
||||
# 1. 序列化结果中的中文可以直接阅读;
|
||||
# 2. JSON中的true、false和null使用小写;
|
||||
# 3. 反序列化后的对象类型为dict;
|
||||
# 4. 合法请求能够输出书名、作者和价格;
|
||||
# 5. 三段无效JSON都得到中文错误提示。
|
||||
|
||||
|
||||
# 自查清单:
|
||||
# 1. 是否导入Python标准库json?
|
||||
# 2. 是否使用json.dumps()而不是str(dict)?
|
||||
# 3. JSON文本中的属性名和字符串是否使用双引号?
|
||||
# 4. 是否理解True与true、None与null的区别?
|
||||
# 5. 是否捕获了json.JSONDecodeError?
|
||||
# 6. 是否没有直接序列化Decimal、datetime或自定义对象?
|
||||
# 7. HTTP请求与响应是否正确声明Content-Type?
|
||||
|
||||
|
||||
# 最终验收标准:
|
||||
# 1. practice.py通过语法检查并可独立运行;
|
||||
# 2. Python对象能够转换为格式化中文JSON;
|
||||
# 3. 合法JSON能够转换为Python字典;
|
||||
# 4. 三类格式错误都能安全处理;
|
||||
# 5. 能解释Python对象、JSON文本和网络字节的区别;
|
||||
# 6. 能指出HTTP报文中的JSON正文和Content-Type。
|
||||
Reference in New Issue
Block a user