python进阶系列 - 10 如何处理JSON

90 阅读5分钟

让天下没有难学的Python!

JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。 在 Python 中,有内建的json模块用于编码和解码JSON数据。

使用时直接导入json模块,就可以开始工作于 JSON 数据,如下代码:

import json

JSON 数据的优点:

  • JSON 是一个字节序列,这对于我们需要传输(流)数据的情况非常有用。
  • 与 XML 比较,JSON 比较小,转换成更快的数据传输,更好的体验。
  • JSON 是一种文本格式,同时也是一种机器友好的格式。

JSON 格式

下面是一个json数据的格式,里面有列表、字典、字符串等:

{
  "firstName": "Jane",
  "lastName": "Doe",
  "hobbies": [
    "running",
    "swimming",
    "singing"
  ],
  "age": 28,
  "children": [
    {
      "firstName": "Alex",
      "age": 5
    },
    {
      "firstName": "Bob",
      "age": 7
    }
  ]
}

Json 支持基本类型(字符串,数字,布尔),以及嵌套数组和对象。

简单的 Python 对象转换为 JSON 根据以下转换:

PythonJSON
dictobject
list, tuplearray
strstring
int, long, floatnumber
Truetrue
Falsefalse
Nonenull

从 Python 到 JSON

要转换 Python 对象为 JSON 字符串,请使用 json.dumps() 方法。 示列代码:

import json

person = {
    "name": "John",
    "age": 30,
    "city": "New York",
    "hasChildren": False,
    "titles": ["engineer", "programmer"],
}
# 转换为 JSON 字符串
person_json = json.dumps(person)
# 指定其他参数。
person_json2 = json.dumps(
    person,
    indent=4,
    separators=("; ", "= "),
    sort_keys=True,
)
# 打印:
print(person_json)
print(person_json2)

结果:

{"name": "John", "age": 30, "city": "New York", "hasChildren": false, "titles": ["engineer", "programmer"]}
{
    "age"= 30; 
    "city"= "New York"; 
    "hasChildren"= false; 
    "name"= "John"; 
    "titles"= [
        "engineer"; 
        "programmer"
    ]
}

或者使用json.dump() 方法将 Python 对象转换为 JSON 对象并将其保存到文件中。 示列代码:

import json

person = {
    "name": "John",
    "age": 30,
    "city": "New York",
    "hasChildren": False,
    "titles": ["engineer", "programmer"],
}
with open("./person.json", "w") as f:
    json.dump(person, f)  # 其他参数也可以设定。

从 JSON 到 Python

转换 JSON 字符串为 Python 对象,请使用 json.loads() 方法。 结果将是一个 Python 对象。 示列代码:

import json

# json字符串
person_json = """
{
    "age": 30, 
    "city": "New York",
    "hasChildren": false, 
    "name": "John",
    "titles": [
        "engineer",
        "programmer"
    ]
}
"""
# 转为python对象
person = json.loads(person_json)
print(person)

结果:

{'age': 30, 'city': 'New York', 'hasChildren': False, 'name': 'John', 'titles': ['engineer', 'programmer']}

或者从文件中加载 JSON 对象,并使用 json.load() 方法将其转换为 Python 对象。 示例代码:

import json

with open('./person.json', 'r') as f:
    person = json.load(f)
    print(person)

结果:

{'name': 'John', 'age': 30, 'city': 'New York', 'hasChildren': False, 'titles': ['engineer', 'programmer']}

自定义编码解码

编码

编码自定义对象时,使用默认的 JSONEncoder 将会抛出 TypeError。 我们可以指定一个自定义编码函数,该函数将包含类名和所有对象变量的字典。 使用该函数作为 json.dump() 方法的 default 参数。

代码:

import json


def encode_complex(z):
    if isinstance(z, complex):
        # 仅仅是类名的键是重要的,值可以是任意的。
        return {z.__class__.__name__: True, "real": z.real, "imag": z.imag}
    else:
        raise TypeError(f" '{z.__class__.__name__}' 不是指定的格式!")


z = 5 + 9j
zJSON = json.dumps(z, default=encode_complex)
print(zJSON)

结果:

{"complex": true, "real": 5.0, "imag": 9.0}

你也可以创建一个自定义的编码器类,并覆盖 default() 方法。 使用该类作为 json.dump() 方法的 cls 参数,或者直接使用编码器。

示例代码:

import json
from json import JSONEncoder


class ComplexEncoder(JSONEncoder):
    def default(self, o):
        if isinstance(z, complex):
            return {z.__class__.__name__: True, "real": z.real, "imag": z.imag}
        # 让父类的默认方法处理其他对象,或者抛出一个 TypeError
        return JSONEncoder.default(self, o)


z = 5 + 9j
zJSON = json.dumps(z, cls=ComplexEncoder)
print(zJSON)

# 或者直接使用编码器
zJson = ComplexEncoder().encode(z)
print(zJSON)

结果:

{"complex": true, "real": 5.0, "imag": 9.0}
{"complex": true, "real": 5.0, "imag": 9.0}

解码

用默认的JSONDecoder解码一个自定义对象是可能的,但它会被解码成一个字典。 写一个自定义的解码函数,它将将字典作为输入,并将字典转为自定义的对象。在json.loads中的object_hook参数上使用这个函数。

如下实例:

import json

zJSON = '{"complex": true, "real": 5.0, "imag": 9.0}'

z = json.loads(zJSON)
# 默认为字典类型 
print(type(z))
print(z)


def decode_complex(dct):
    if complex.__name__ in dct:
        return complex(dct["real"], dct["imag"])
    return dct


# 现在,解码后的对象是复合类型的
z = json.loads(zJSON, object_hook=decode_complex)
print(type(z))
print(z)

输出为:

<class 'dict'>
{'complex': True, 'real': 5.0, 'imag': 9.0}
<class 'complex'>
(5+9j)

通用对象解码、编码函数

下面展示的代码可用于所有自定义类,如果所有类变量都在 __init__ 方法中提供。

import json


class User:
    # 自定义类,并且在__init__()中指定所有的类变量
    def __init__(self, name, age, active, balance, friends):
        self.name = name
        self.age = age
        self.active = active
        self.balance = balance
        self.friends = friends


class Player:
    # 另外一个自定义类
    def __init__(self, name, nickname, level):
        self.name = name
        self.nickname = nickname
        self.level = level


def encode_obj(obj):
    """
    接受一个自定义对象并返回该对象的字典表示。
    此 dict 表示还包括对象的模块和类名。
    """
    # 将对象的模块和类名添加到字典中
    obj_dict = {
        "__class__": obj.__class__.__name__,
        "__module__": obj.__module__,
    }
    # 用对象属性更新字典
    obj_dict.update(obj.__dict__)
    return obj_dict


def decode_dct(dct):
    """
    接受一个字典并返回一个与该字典关联的自定义对象。
    它利用字典中的“__module__”和“__class__”元数据
    知道要创建哪种对象类型。
    """
    if "__class__" in dct:
        # 将字典中的“__module__”和“__class__”元数据删除,以便只保留实例参数
        class_name = dct.pop("__class__")
        module_name = dct.pop("__module__")
        # 我们使用内建的__import__函数,因为模块名称在运行时还不知道
        module = __import__(module_name)
        # 在模块中查找类名
        class_ = getattr(module, class_name)
        # 用字典解包的方式初始化对象
        # 注意:这只在类的所有__init__()参数都是字典键时才能工作
        obj = class_(**dct)
    else:
        obj = dct
    return obj


# User 类使用我们的编码和解码方法
user = User(
    name="John",
    age=28,
    friends=["Jane", "Tom"],
    balance=20.70,
    active=True,
)
userJSON = json.dumps(
    user,
    default=encode_obj,
    indent=4,
    sort_keys=True,
)
print(userJSON)
user_decoded = json.loads(
    userJSON,
    object_hook=decode_dct,
)
print(type(user_decoded))
# Player 类使用我们的编码和解码方法
player = Player("Max", "max1234", 5)
playerJSON = json.dumps(
    player,
    default=encode_obj,
    indent=4,
    sort_keys=True,
)
print(playerJSON)
player_decoded = json.loads(playerJSON, object_hook=decode_dct)
print(type(player_decoded))

输出:

{
    "__class__": "User",
    "__module__": "__main__",
    "active": true,
    "age": 28,
    "balance": 20.7,
    "friends": [
        "Jane",
        "Tom"
    ],
    "name": "John"
}
<class '__main__.User'>
{
    "__class__": "Player",
    "__module__": "__main__",
    "level": 5,
    "name": "Max",
    "nickname": "max1234"
}
<class '__main__.Player'>

小节

本文介绍了python中json模块的用法,相信大部分是不知道如何自定义json的编码、解码函数的。

巧用json的自定义编码、解码函数,可以将任意类型对象进行json.dump()。这样在对复杂对象处理时非常有用!

感谢你阅读本文。欢迎大家点赞、收藏、支持!

pythontip 出品,Happy Coding!

公众号: 夸克编程