JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,用于在不同应用程序之间传递数据。它是一种文本格式,易于阅读和编写,同时也易于解析和生成。JSON最初是由Douglas Crockford于2001年提出的,它基于JavaScript对象字面量语法,但已经成为一种独立于编程语言的数据格式。
乍一看json对象和python中的字典长得差不多,但前者是一种数据格式,而后者是一种数据结构。
具体来讲,json对象由一个大括号 {}
组成,大括号里保存的是若干个key-value对,其中key必须是字符串,且必须由双引号括起来(python的字典可以是单引号)。value可以是字符串,数值(整数/浮点数),布尔值,null,数组,json对象。key和value使用冒号 :
进行分隔,每个key-value对使用逗号 ,
进行分隔。
json对象通常存储在以 .json
为扩展名的文本文件中,一个可能的例子:
{
"k1": "a",
"k2": 1,
"k3": 1.1,
"k4": true,
"k5": false,
"k6": null,
"k7": [
1,
1.1,
false,
"b"
],
"k8": {
"c": 2,
"d": 3
}
}
以上列出了value所有可能的数据类型。当然,json文件中可以包含多个json对象,通常由一个大列表括起来,每个json对象间用逗号分隔:
[
{
"k1": 1,
"k2": 2
},
{
"k3": 3,
"k4": 4
}
]
事实上,json文件能存储的有:
对于数组和json对象,其中的最后一个元素后面不能有逗号。
json
库是python的标准库,它主要有四个函数:dump
、dumps
、load
、loads
,接下来也只讲解这四个函数。
dumps用于将一个python对象转化成json格式的字符串,其中python对象必须是json可序列化的,即上面提到的三种。dumps这一过程又被称为序列化。
不严谨地来讲,dumps的函数签名如下:
def dumps(obj: Union[None, int, float, str, bool, list, dict], *args) -> str:
pass
一些示例:
print(json.dumps(None))
# null
print(json.dumps(1))
# 1
print(json.dumps(3.3))
# 3.3
print(json.dumps("abc"))
# "abc"
print(json.dumps([1, 2, 3, 4]))
# [1, 2, 3, 4]
print(json.dumps({"a": 1, "b": 2}))
# {"a": 1, "b": 2}
loads的作用与dumps相反,loads过程又被称为反序列化,即将一个json格式的字符串转化为相应的python对象。
不严谨地来讲,loads的函数签名如下:
def loads(s: str, *args) -> Union[None, int, float, str, bool, list, dict]:
pass
s
还可以是bytes或bytearray,这超出了本文的讨论范围。
一些示例:
print(json.loads("null"))
# None
print(json.loads("1"))
# 1
print(json.loads("3.3"))
# 3.3
print(json.loads("\"abc\""))
# abc
print(json.loads("[1, 2, 3, 4]"))
# [1, 2, 3, 4]
print(json.loads("{\"a\": 1, \"b\": 2}"))
# {'a': 1, 'b': 2}
在使用dumps时有一个小细节,如下:
print(json.dumps("abc你好"))
# "abc\u4f60\u597d"
默认情况下,所有非 ASCII 字符(即那些无法用标准 ASCII 表示的字符)都被转义为 Unicode 转义序列。这样做的目的是为了确保生成的 JSON 字符串是 ASCII 编码的,因为一些旧系统或处理 JSON 数据的应用程序可能不支持非 ASCII 字符。
如果要防止对非ASCII字符进行转义,可以设置 ensure_ascii = False
,如下:
print(json.dumps("abc你好", ensure_ascii=False))
# "abc你好"
dump用于将一个python对象(需要是json可序列化的)保存到相应的json文件中,dump没有返回值。
with open('1.json', 'w') as f:
json.dump([1, 2, 3], f)
load用于从一个json文件中读取并转化为相应的python对象:
with open('1.json') as f:
print(json.load(f))
# [1, 2, 3]
JSONL(JSON Lines)中的每一行都是一个json对象,如下:
{"name": "John", "age": 30, "city": "New York"}
{"name": "Alice", "age": 25, "city": "Los Angeles"}
{"name": "Bob", "age": 35, "city": "Chicago"}
JSONL 更适合处理大型数据集,特别是日志文件和行格式数据。它可以逐行处理,而不需要一次性加载整个文件到内存中。这使得它在处理大型数据文件时更具效率。
使用json库对jsonl文件进行读写:
# 读取
with open('1.jsonl') as r:
for line in r:
print(json.loads(line))
# 写入
content = ... # 预定义
with open('1.jsonl', 'w') as w:
for line in content:
w.write(json.dumps(line, ensure_ascii=False) + '\n')
使用 jsonlines
库,我们可以更高效的处理jsonl文件:
import jsonlines
# 读取
with jsonlines.open('1.jsonl') as r:
for line in r:
print(type(line), line)
# {'name': 'John', 'age': 30, 'city': 'New York'}
# {'name': 'Alice', 'age': 25, 'city': 'Los Angeles'}
# {'name': 'Bob', 'age': 35, 'city': 'Chicago'}
# 写入
dic = {"a": 1, "b": 2}
with jsonlines.open('1.jsonl', 'w') as w:
w.write(dic) # 会自动加换行符
# 写入多个
dic = [{"a": 1, "b": 2}, {"c": 3, "d": 4}]
with jsonlines.open('1.jsonl', 'w') as w:
w.write_all(dic)
可以看到我们能够直接对 dict
对象进行操作,而不需要进行 dict <-> str
的转化。
write()
有返回值,即当前行的字符总数(包括换行符)。write_all()
是所有行的字符总数。