Json是是一种轻量级的数据交换格式,易于人阅读和编写,现在大多服务的接口数据都使用Json进行传递。测试人员与Json免不了打交道。

Python官方提供了Json库供用户处理Json数据,包括4种方法:

  • dumps()
  • loads()
  • dump()
  • load()

使用前要导入json模块:

import json

这4种方法基本可以满足测试人员的需求。如果有性能更好、功能更多的需求,可以使用第三方库,如orjson、rapidjson、Pydantic。

本篇详细介绍如何使用json.dumps()方法。

关于json.dumps()

json.dumps()能够将Python对象编码成JSON格式的字符串。它接受一个Python对象作为参数,然后将其转换成JSON格式的字符串并返回,方便被其他程序或者编程语言读取。

python 原始类型向 json 类型的转化对照表:

Python JSON
dict object
list, tuple array
str, unicode string
int, long, float number
True true
False false
None null

json.dumps()方法的基本用法:

json.dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False)

json.dumps()的各个参数中,obj是必选的,其他参数可选。

obj 参数

obj可以是python的各种对象,比如列表、字典、元组、字符串等

import json

# 假设我们有一个字典
data = {
    'name': 'JZY',
    'age': 18,
    'city': 'HeNan'
}
print(data)  # 输出data
print(type(data))  # 将输出data的类型为dict(字典)
json_string = json.dumps(data)  # data就是obj
print(json_string)  # 输出转换后的data,即json_string
print(type(json_string))  # 将输出json_string类型为json格式的字符串

运行结果

{'name': 'JZY', 'age': 18, 'city': 'HeNan'}
<class 'dict'>
{"name": "JZY", "age": 18, "city": "HeNan"}
<class 'str'>

skipkeys 参数

默认为False。设置为True时,如果遇到字典中的键不是基本类型(即不是str, unicode, int, long, float, bool, None),则跳过该键值对不进行序列化,而不是抛出TypeError异常。skipkeys设置为False(默认值),如果字典中包含非基本类型的键,则json.dumps()会抛出TypeError异常。

import json

# 假设我们有一个字典,其中包含一个非基本类型的键,即一个元组
data = {
    'a': 1,
    ('b', 'c'): 2,  # 这是一个元组作为键,不是基本类型
    'd': 3
}

try:
    # 默认情况下,这将抛出TypeError
    json.dumps(data)
except TypeError as e:
    print("TypeError:", e) # 输出错误类型

# 使用skipkeys=True避免错误
json_data = json.dumps(data, skipkeys=True)
print(json_data)  # 输出将只包含键'a'和'd',因为('b', 'c')不是基本类型

运行结果

TypeError: keys must be str, int, float, bool or None, not tuple
{"a": 1, "d": 3}

在上面的例子中,如果没有使用skipkeys=True,尝试序列化包含元组作为键的字典将会引发一个TypeError。使用skipkeys=True可以避免这个错误,但会丢失那些无法序列化的键值对。

ensure_ascii 参数

默认为True。设置为True(默认值)时,所有的非ASCII字符(即Unicode字符)都会被转义成ASCII字符。这意味着输出的JSON字符串将只包含ASCII字符,这确保了最大兼容性,因为ASCII是所有Unicode字符集的子集。设置为False时,json.dumps()会尽可能地输出原始的Unicode字符,而不是使用转义序列。这可以使得输出的字符串更短,且在某些情况下更易于阅读,特别是当处理包含大量非ASCII字符的数据时。

import json

# 假设我们有一个包含非ASCII字符的字符串,ü非ASCII字符,汉字非ASCII字符。
data = {
    'name': 'Günter',
    'city': '北京'
}

# 默认情况下,ensure_ascii=True,非ASCII字符会被转义
print(json.dumps(data))

# 使用ensure_ascii=False输出原始Unicode字符
print(json.dumps(data, ensure_ascii=False))

运行结果

{"name": "G\u00fcnter", "city": "\u5317\u4eac"}
{"name": "Günter", "city": "北京"}

这个例子中,第一个json.dumps()调用回转义非ASCII字符,因为默认ensure_ascii=True。 第二个json.dumps()由于设置ensure_ascii=False,不会转义非ASCII字符。

check_circular 参数

默认为True。设置为True(默认值),json.dumps()会检查对象中是否存在循环引用,即对象直接或间接地引用自己。如果发现循环引用,将抛出ValueError异常。设置为False时,json.dumps()不会进行循环引用的检查。这可以提高序列化的性能,但代价是在对象中存在循环引用时可能会导致无限递归。

import json

# 创建一个包含循环引用的字典
data = {}
data['self'] = data

try:
    # 默认情况下,check_circular=True,会抛出ValueError:Circular reference detected(检测到循环引用)
    json.dumps(data)
except ValueError as e:
    print("ValueError:", e)

try:
    # 设置check_circular=False,不会抛出ValueError,
    # 但会抛出RecursionError:maximum recursion depth exceeded while encoding a JSON object
    # 翻译成中文是RecursionError:在对JSON对象进行编码时超过了最大递归深度。
    json.dumps(data, check_circular=False)
except RecursionError as e:
    print("RecursionError:", e)

这个例子中,第一个json.dumps()调用会抛出ValueError,因为默认check_circular为True,检测到了循环引用。 第二个调用尝试通过设置check_circular=False来避免ValueError,但由于存在循环引用,最终会抛出RecursionError。

运行结果

ValueError: Circular reference detected
RecursionError: maximum recursion depth exceeded while encoding a JSON object

allow_nan 参数

默认为True。设置为True(默认值)时,json.dumps()允许将NaN(Not a Number)、Infinity和-Infinity这样的特殊浮点数值序列化为它们的字符串表示形式。这与JavaScript的JSON相兼容,因为JavaScript的JSON解析器能够理解这些值。设置为False时,如果尝试序列化NaN、Infinity或-Infinity,json.dumps()将抛出ValueError异常。

import json

data = {
    'normal_number': 3.14,
    'nan': float('nan'),
    'infinity': float('inf'),
    'negative_infinity': -float('inf')
}

# 默认情况下,allow_nan=True,特殊浮点数值被允许
print(json.dumps(data))

try:
    # 设置allow_nan=False,将抛出ValueError:Out of range float values are not JSON compliant(超出范围的浮点数值不符合JSON标准)
    json.dumps(data, allow_nan=False)
except ValueError as e:
    print("ValueError:", e)

运行结果

{"normal_number": 3.14, "nan": NaN, "infinity": Infinity, "negative_infinity": -Infinity}
ValueError: Out of range float values are not JSON compliant: nan

在第一个输出中,特殊浮点数值被序列化为它们的字符串表示形式。在第二个尝试中,由于设置了allow_nan=False,尝试序列化包含NaN、Infinity或-Infinity的data字典将抛出ValueError。

cls 参数

默认为Nne。一个可选参数,用于指定一个自定义的JSONEncoder类。如果你提供了这个参数,json.dumps()会使用这个类来编码对象。

import json
from datetime import datetime

# 创建一个包含datetime对象的字典
data = {
    'name': 'Alice',
    'created_at': datetime.now()
}


try:
    # 默认情况下,`json.dumps()`无法序列化datetime对象,
    # 将抛出ValueError:Object of type datetime is not JSON serializable
    # 翻译成中文:ValueError:日期时间类型的数据无法被序列化为JSON
    json_data = json.dumps(data)
except TypeError as e:
    print("ValueError:", e)


# 自定义JSONEncoder类
class CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()  # 将datetime对象序列化为ISO格式的字符串
        return json.JSONEncoder.default(self, obj)


# 使用自定义的JSONEncoder类来序列化datetime对象
json_data = json.dumps(data, cls=CustomEncoder)
print(json_data)

使用cls参数时,你需要定义一个继承自json.JSONEncoder的类,并重写一些方法来实现自定义的序列化逻辑。

在这个例子中,CustomEncoder类重写了default()方法,以支持datetime对象的序列化。 当json.dumps()被调用并传入CustomEncoder参数,它将使用了CustomEncoder来将datetime对象转换为ISO格式的字符串,从而使得整个字典可以被成功序列化。

运行结果

ValueError: Object of type datetime is not JSON serializable
{"name": "Alice", "created_at": "2024-08-26T17:28:42.466506"}

indent 参数

默认为None。一个可选参数,用于指定每个层级的缩进空格数。如果设置为非负整数,则序列化后的JSON数据将被格式化以便于阅读,每个层级的缩进为指定的空格数。如果设置为None(默认值),则输出的JSON数据将尽可能紧凑,没有额外的空格或换行符。

import json

# 创建一个嵌套的字典
data = {
    'name': 'JZY',
    'age': 18,
    'is_student': False,
    'courses': ['1', 2, '3'],
    'address': {
        'street': 'JZY',
        'city': 'JZY',
        'zip': '12345'
    }
}

# 使用indent参数进行美化输出
pretty_json = json.dumps(data, indent=4)
print(pretty_json)

运行结果

{
    "name": "JZY",
    "age": 18,
    "is_student": false,
    "courses": [
        "1",
        2,
        "3"
    ],
    "address": {
        "street": "JZY",
        "city": "JZY",
        "zip": "12345"
    }
}

在这个例子中,通过设置indent=4,输出的JSON数据具有4个空格的缩进,使得数据结构更加清晰。 这种格式化的输出在编写和调试代码时非常有用,尤其是在处理复杂的数据结构时。然而,这种格式化会增加输出的大小,因此在需要最小化数据传输量的情况下,可能不适用。

separators 参数

默认为None。一个可选参数,它应该是一个包含两个字符串的元组:第一个用于字段之间的分隔符(默认是,),第二个用于字段与值之间的分隔符(默认是:)。默认情况下,json.dumps()使用的分隔符是(", ", ": "),这意味着字段之间和字段与值之间会有一个空格。 通过自定义separators,你可以生成没有额外空格的JSON数据,这在需要最小化数据大小的场景下非常有用。

import json

data = {
    'name': 'JZY',
    'age': 18,
    'is_student': False
}

# 使用默认的分隔符(带空格)
default_json = json.dumps(data)
print(default_json)

# 使用自定义的分隔符(无空格)
compact_json = json.dumps(data, separators=(",", ":"))
print(compact_json)

运行结果

{"name": "JZY", "age": 18, "is_student": false}
{"name":"JZY","age":18,"is_student":false}

在第一个输出中,使用了默认的分隔符,字段之间和字段与值之间都有一个空格。在第二个输出中,通过设置separators=(",", “:"),生成了更紧凑的JSON数据,没有额外的空格。

default 参数

默认为None。一个可选参数,接受一个函数。当json.dumps()遇到无法直接序列化的对象时(例如,自定义对象、datetime对象等),它会调用这个函数来获取一个可序列化的替代值。

import json
from datetime import datetime


# 定义一个自定义的序列化函数,函数检查传入的对象是否为datetime类型,并将其转换为ISO格式的字符串。
# 如果对象不是datetime类型,且没有其他方式可以序列化,函数将抛出TypeError异常。
def default_serializer(obj):
    if isinstance(obj, datetime):
        return obj.isoformat()  # 将datetime对象转换为ISO格式的字符串
    raise TypeError(f"Type {type(obj)} not serializable")


# 创建一个包含datetime对象的字典
data = {
    'name': 'Alice',
    'created_at': datetime.now()
}


# 使用default参数指定自定义的序列化函数
json_data = json.dumps(data, default=default_serializer)
print(json_data)

{"name": "Alice", "created_at": "2024-08-27T09:35:19.642028"}

在这个例子中,default_serializer函数检查传入的对象是否为datetime类型,并将其转换为ISO格式的字符串。如果对象不是datetime类型,且没有其他方式可以序列化,函数将抛出TypeError异常。

当然你也可以在default_serializer函数中自定义一个解码器将那些无法直接序列化的Python对象转换为JSON格式。

sort_keys 参数

默认为False。设置为True时,序列化后的JSON数据中的键将按照字典序(通常是字母序或数字序)进行排序。 设置为False(默认值)时,键将按照它们在Python字典中出现的顺序进行序列化。从Python 3.7开始,字典是有序的,这意味着在同一个Python程序中序列化同一个字典对象将产生一致的结果。

import json

# 创建一个字典,键的顺序可能不是字典序
data = {
    'name': 'JZY',
    'created_at': '2024-08-27T12:00:00',
    'age': 18,
    'is_student': False
}

# 不使用sort_keys参数(默认为False),保持键的原始顺序
json_data_unsorted = json.dumps(data)
print(json_data_unsorted)

# 使用sort_keys参数进行排序
json_data_sorted = json.dumps(data, sort_keys=True)
print(json_data_sorted)

运行结果

{"name": "JZY", "created_at": "2024-08-27T12:00:00", "age": 18, "is_student": false}
{"age": 18, "created_at": "2024-08-27T12:00:00", "is_student": false, "name": "JZY"}

在第一个输出中,键的顺序是根据它们在字典中出现的顺序进行序列化的。在第二个输出中,通过设置sort_keys=True,键被按照字典序排序

请求中使用json.dumps()

首先安装requests库。

from datetime import datetime
import requests
import json


# 自定义JSONEncoder类
class CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()  # 将datetime对象序列化为ISO格式的字符串
        return json.JSONEncoder.default(self, obj)


# 定义请求的URL
url = 'http://example.com/api'

# 定义请求体的数据
data = {
    'key1': 'value1',
    'date': datetime.now()
}

# 将字典转换为JSON格式
json_data = json.dumps(data, cls=CustomEncoder)

# 发送POST请求,headers中指定了请求体的格式为application/json
response = requests.post(url, headers={'Content-Type': 'application/json'}, data=json_data)

# 打印响应的内容
print(response.text)

使用requests.post(url, json=data)发送请求时, json=data 会自动将数据转换为 JSON 格式,并设置正确的Content-Type 头部(application/json),但不会转换date格式的数据。 所以需要使用json.dumps()转换date为字符串,并使用response = requests.post(url, headers={'Content-Type': 'application/json'}, data=json_data)发送请求。

json.dump() 方法

json.dumps()能够 Python 对象序列化为 JSON 格式的字符串,并将这个字符串写入到一个文件中。 它接受至少两个参数,第一个参数是要序列化的 Python 对象,第二个参数是一个文件对象,这个文件对象应该已经处于写入模式(‘w’ 或 ‘wb’)。

json.dump()方法的基本用法:

json.dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, encoding="utf-8", default=None, sort_keys=False)

json.dump()的各个参数中,obj和fp是必选的,其他参数可选。json.dump()json.dumps()的唯一区别是json.dump()是将数据写进文件内,多了fp参数。 对于其他参数,json.dump()json.dump()的用法是一样的,不再赘述。

import json

# 假设我们有一个字典
data = {
    'name': 'JZY',
    'age': 18,
    'address': 'HeNan'
}

# 将数据写入到到py文件当前目录下的data.json文件中
# 如果data.json不存在,自动创建
with open('data.json', 'w') as f:  
    json.dump(data, f)

运行结果

这个方法非常适合于将数据持久化存储到文件,或者通过网络发送到其他服务。


THEEND



© 转载需要保留原始链接,未经明确许可,禁止商业使用。CC BY-NC-ND 4.0