JSON and JSONL: Objects, Arrays, and Streaming Records
什么是JSON
JS = JavaScript ; ON = Object Notation , 故JSON的本身含义是JavaScript 对象符号系统,但是他已经兼容了几乎一切语言形式,在任何同时需要机器和人类进行双重阅读的时候,都会使用JSON 其使用.json 表示作为后缀.
JSON使用 {}作为核心对象表示符号,并且全部使用键值对组织数据,对于一个对象里面的各个子列,我们使用逗号分隔,除了最后一个.
1 2 3 4 5
| { "name" : "Jake", "age" : 25, "hobbies":["Swimming","Basketball"] }
|
JSON允许嵌套,可以在一个JSON对象的值中嵌套其他对象,这使得我们可以使用多层的嵌套来管理和使用复杂的数据.
除了这种使用{}的结构以外, JSON还有一种特殊的对象数组结构,专门为存储大量类似格式的重复条目设计,他直接在JSON文件中使用 [] 存储一个个数组中的对象,由于支持自然的嵌套,这些对象往往也是JSON形式的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| [ { "id": 1, "text": "这是我的第一篇博文,内容很精彩。", "images": [ "/posts/2023/10/20/img1.jpg", "/posts/2023/10/20/img2.jpg" ], "timestamp": "2023-10-20T10:00:00Z", "location": "北京" }, { "id": 2, "text": "今天去了长城,风景真不错。", "images": [ "/posts/2023/10/21/great_wall_a.jpg", "/posts/2023/10/21/great_wall_b.jpg", "/posts/2023/10/21/great_wall_c.jpg" ], "timestamp": "2023-10-21T15:30:00Z", "location": "北京, 八达岭长城" }, ]
|
JSONL文件
当单个JSON文件(一般是对象数组的JSON文件)变得非常巨大(例如几百MB或几GB),一次性读入内存不现实时,可以使用JSON Lines格式, .jsonl 他用来处理类似对象数组的问题.
.jsonl 文件中,每一行都是一个独立的、完整的JSON对象,行与行之间没有逗号,整个文件也没有外层的方括号 []. 注意JSONL文件中的JSON对象不嵌套使用,我们不用他来处理需要多层解析的问题
1 2 3 4 5 6
| {"id": 1, "text": "这是我的第一篇博文,内容很精彩。", "images": ["/posts/2023/10/20/img1.jpg", "/posts/2023/10/20/img2.jpg"], "timestamp": "2023-10-20T10:00:00Z", "location": "北京"}
{"id": 2, "text": "今天去了长城,风景真不错。", "images": ["/posts/2023/10/21/great_wall_a.jpg", "/posts/2023/10/21/great_wall_b.jpg", "/posts/2023/10/21/great_wall_c.jpg"], "timestamp": "2023-10-21T15:30:00Z", "location": "北京, 八达岭长城"}
{"id": 3, "text": "分享一个技术心得...", "images": [], "timestamp": "2023-10-22T09:00:00Z", "location": "上海"}
|
.jsonl 格式支持流式处理,可以逐行读取和处理,不需要一次性加载整个文件到内存,内存占用极低.并且易于追加,直接在末尾追加一行就可以.
.jsonl 格式本质上是多个 JSON 文件, 无法随机访问,只能从第一行开始逐个访问. 并且无法直接使用类似JSON的方法解析.
如何处理JSON
我们最经常使用JS和Python来处理JSON格式的数据,其中JSON.parse这个原生支持的JS函数可以将它处理为JS支持的对象,之后可以选择使用方括号或者点号来根据访问其中的嵌套对象.
下面详细解释一下Python中json模块里这两个非常重要且容易混淆的函数:json.loads() 和 json.load(). 他们将JSON文件转换为Python字典来处理,后续使用Python字典的方括号来处理. 解析错误的时候发生 json.JSONDecodeError
一句话总结核心区别:s 代表 string(字符串). json.loads() 用于解析字符串,而 json.load() 用于解析文件.
json.loads() 将 str, bytes 或 bytearray 类型的实例,其中包含JSON文档,反序列化为Python对象. 一般用于API的响应的处理.
1 2 3 4
| import json
json.loads(s, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
|
一般遵循下面的转化方法
- object
{} -> dict dict
- array
[] -> list list
- string
"" -> str str
- number (int) -> int
int
- number (real) -> float
float
true -> True
false -> False
null -> None
load 的作用是从一个文件类对象中读取JSON数据,并将其解析成Python对象.这里的“文件类对象”指的是任何支持 .read() 方法的对象,最常见的就是通过 open() 函数打开的文件.
1 2 3 4
| import json
json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
|
使用下面的代码,需要先进行打开文件,然后再使用 json.load 处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import json
file_path = 'data.json'
try: with open(file_path, 'r', encoding='utf-8') as f: python_data = json.load(f)
print("从文件解析后的Python对象:") print(python_data) print(f"类型: {type(python_data)}")
print(f"用户邮箱: {python_data['email']}") print(f"标签数量: {len(python_data['tags'])}")
except FileNotFoundError: print(f"错误:文件 {file_path} 未找到。") except json.JSONDecodeError as e: print(f"JSON解析错误: {e}")
|
如何处理JSONL
首先明确一点,整个JSONL文件本身不是一个有效的 JSON 对象或数组.不能用 json.load() 一次性读取整个文件,因为它会报错,说文件格式不正确.正确的做法是逐行读取,并对每一行使用 json.loads().
假设有JSONL文件
1 2 3 4 5
| # data.jsonl {"name": "Alice", "age": 30, "city": "New York"} {"name": "Bob", "age": 25, "city": "Los Angeles"} {"name": "Charlie", "age": 35, "city": "Chicago"}
|
用 Python 来读取它,当然这只是一个例子,不够Robustness:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import json
data_list = []
with open('data.jsonl', 'r', encoding='utf-8') as f: for line in f: data = json.loads(line) data_list.append(data) print(f"读取到一行数据: {data}")
print("\n所有数据已加载到列表中:") print(data_list)
|
如同JSONL本身的特性一样,我们最终获得了一个Python中的字典列表
写JSON
写标准.json , 当你有一个完整的数据结构(比如一个包含所有博文的列表),并希望将它存储为一个单一的、格式化的JSON文件时,使用此方法.
关键函数: json.dump(data, file_object)
data: 你要写入的Python对象(字典、列表等)
file_object: 一个已打开的文件对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import json
blog_posts = [ { "id": 1, "text": "今天天气真好", "images": ["sunny.jpg"], "timestamp": "2023-10-20T10:00:00Z" }, { "id": 2, "text": "分享一篇好文章", "images": [], "timestamp": "2023-10-20T11:30:00Z" }, { "id": 3, "text": "我的新宠物", "images": ["cat1.jpg", "cat2.jpg"], "timestamp": "2023-10-20T12:15:00Z" } ]
with open('posts.json', 'w', encoding='utf-8') as f: json.dump(blog_posts, f, indent=4, ensure_ascii=False)
print("成功写入 posts.json 文件")
|
生成的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| [ { "id": 1, "text": "今天天气真好", "images": [ "sunny.jpg" ], "timestamp": "2023-10-20T10:00:00Z" }, { "id": 2, "text": "分享一篇好文章", "images": [], "timestamp": "2023-10-20T11:30:00Z" }, { "id": 3, "text": "我的新宠物", "images": [ "cat1.jpg", "cat2.jpg" ], "timestamp": "2023-10-20T12:15:00Z" } ]
|
写.jsonl 当需要流式处理数据,或者数据量非常大,不希望一次性加载到内存时,使用JSONL.每一行都是一个独立的JSON对象.
关键函数: json.dumps(data)
data: 你要转换的Python对象(通常是单个字典)
- 该函数返回一个JSON格式的字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| import json
blog_posts = [ { "id": 1, "text": "今天天气真好", "images": ["sunny.jpg"], "timestamp": "2023-10-20T10:00:00Z" }, { "id": 2, "text": "分享一篇好文章", "images": [], "timestamp": "2023-10-20T11:30:00Z" }, { "id": 3, "text": "我的新宠物", "images": ["cat1.jpg", "cat2.jpg"], "timestamp": "2023-10-20T12:15:00Z" } ]
with open('posts.jsonl', 'w', encoding='utf-8') as f: for post in blog_posts: json_string = json.dumps(post, ensure_ascii=False) f.write(json_string + '\n')
print("成功写入 posts.jsonl 文件")
|
生成的结果
1 2 3 4
| {"id": 1, "text": "今天天气真好", "images": ["sunny.jpg"], "timestamp": "2023-10-20T10:00:00Z"} {"id": 2, "text": "分享一篇好文章", "images": [], "timestamp": "2023-10-20T11:30:00Z"} {"id": 3, "text": "我的新宠物", "images": ["cat1.jpg", "cat2.jpg"], "timestamp": "2023-10-20T12:15:00Z"}
|