跳转到主要内容

类型安全的JSON(反)序列化

项目描述

typjson

Python的类型安全JSON(反)序列化。与mypy类型提示兼容。

需求

  • Python 3.7或更高版本

特性

  • 运行时类型安全以及与mypy兼容
  • 支持开箱即用的类型
    • 原始类型
      • str, int, float, bool, Decimal, None
      • date作为"%Y-%m-%d"datetime作为"%Y-%m-%dT%H:%M:%S%z"time作为"%H:%M:%S"
      • UUID作为格式为"8-4-4-4-12"str
      • char类型作为长度为1的str
    • Union[]和因此Optional[]
    • 结构类型:List[]Tuple[]Dict[str, T]Set[]
    • 枚举类
    • 数据类
  • 支持自定义编码器和解码器
  • API类似于标准json模块

简单用法

from typ import json
from typing import *
from datetime import date
from dataclasses import dataclass


@dataclass
class Address:
    street: str
    house: int
    apt: Optional[str]


@dataclass
class Person:
    first_name: str
    last_name: str
    languages: List[str]
    address: Address
    birth_date: date


person = Person(
    "John",
    "Smith",
    ["English", "Russian"],
    Address("Main", 1, "2A"),
    date(year=1984, month=8, day=1)
)

json_str = json.dumps(person, indent=2)
loaded_person = json.loads(Person, json_str)

assert person == loaded_person

上面代码示例中,json_str的值看起来像这样被序列化和反序列化

{
  "first_name": "John",
  "last_name": "Smith",
  "languages": [
    "English",
    "Russian"
  ],
  "address": {
    "street": "Main",
    "house": 1,
    "apt": "2A"
  },
  "birth_date": "1984-08-01"
}

类型安全

运行时

Python中的类型安全是什么?由于Python是动态类型语言,在运行时之前提供任何类型保证都很难。然而,类型可以在运行时进行检查。这正是typjson库所做的。考虑上面定义的Address类型示例

from typ import json
from typing import *
from dataclasses import dataclass


@dataclass
class Address:
    street: str
    house: int
    apt: Optional[str]

json_str = """{"street": "Main", "house": 1, "apt": 2}"""
loaded_address = json.loads(Address, json_str)

apt字段定义为类型为Optional[str],然而JSON中提供的值是2,它是JSON中的number类型,显然与Optional[str]不兼容。相应地,调用json.loads将引发JsonError

typ.encoding.JsonError: Value 2 can not be deserialized as typing.Union[str, NoneType]

调用json.loads将返回请求类型的实例,并检查所有嵌套类型,或者引发错误。这是typjson的运行时类型安全。

编译时间(mypy)

typ.json 模块中,函数 dumpsloadsdumpload 都具有适当的类型提示。因此,可以使用 mypy 工具验证类型。

json_str = """{"street": "Main", "house": 1, "apt": 2}"""
loaded_address = json.loads(Address, json_str)
loaded_address = "some other address"

这将在 mypy 中产生错误,因为 loaded_address 的类型被推断为 Address

error: Incompatible types in assignment (expression has type "str", variable has type "Address")

这提供了编译时的类型安全。

API 概述

typjson API 与 json 模块 API 类似。主要函数定义在 typ.json 模块中。最有用的函数是 typ.json.loadstyp.json.dumps。如果在 JSON 编码/解码过程中出现问题,则引发 JsonError。实际上,typ.json 函数在底层使用 json 模块进行 Python 结构与 JSON 之间的最终转换。

支持的类型列表请参阅此处。"自定义编码"部分描述了如何支持任何类型,除了开箱即用的类型。

支持的类型

原始类型

Python 类型 JSON 类型 注意
int number
float number
decimal.Decimal number
boolean boolean
typ.typing.char string 长度为 1 的字符串
str string
uuid.UUID string 带连字符的小写十六进制符号,格式为 8-4-4-4-12
datetime.date string ISO 8601 yyyy-mm-dd
datetime.datetime string ISO 8601 yyyy-mm-ddThh:mm:ss.ffffff
datetime.time string ISO 8601 hh:mm:ss.ffffff
typ.typing.NoneType
type(None)
null

非原始类型

Python 类型 JSON 类型 注意
List[T] array 同构,元素已编码
Dict[str, T] object T 的字段值已编码
Set[T] array 同构,T 的元素已编码
Tuple[T, K, ...] array 异构,T、K、... 的元素已编码
Union[T, K, ...] 查找 T、K、... T、K、... 已编码
list array 异构,元素已编码
dict object
tuple array 异构,元素已编码
枚举类 查找成员类型 枚举成员根据其类型进行编码
带有装饰器的类
@dataclass
object 字段类型受到尊重
带有装饰器的类
@union
object 具有单个字段的对象
Any 任何类型 任何事物

空值安全

除了 NoneTypetype(None) 之外,所有类型都不能有 None 值。 Optional[T] 允许 None 值。因此,如果需要可空 str,则 Optional[str] 将是一个不错的选择。《Optional[T] 类型实际上是 Union[T, NoneType],因此在 typjson 中它通过 Union[] 支持来实现。因此,Optional[T] 没有在上面列出,因为它只是一个 Union

自定义编码

实际上,所有开箱即用的支持类型都是通过编码器和解码器来支持的。仅提供自定义编码器和解码器的示例以供基本理解。对于更深入的了解,可以查看 typ.encoding 模块的源代码。

自定义编码器

typ.json.dumptyp.json.dumps 函数接受编码器列表作为参数。这些编码器是自定义编码器,它们用于除标准内置编码器之外。让我们实现一个自定义编码器,该编码器将所有整数编码为 JSON 中的字符串。

from typ.encoding import Unsupported, check_type

def encode_int_custom(encoder, typ, value):
    if typ != int:
        # if this encoder is not applicable to the typ it should return Unsupported
        return Unsupported
    # there's a helper function checking that value is instance of specified type - int
    check_type(int, value)
    # return encoded value
    return str(value)

from typ import json
assert json.dumps([3, 4, 5], encoders=[encode_int_custom]) == '["3", "4", "5"]'

在上面的代码中,encode_int_custom 被传递给 typ.json.dumps 调用,并且在标准内置 int 编码之前使用。正如 assert 所示,它成功地以字符串的形式编码了整数。请勿在现实世界中执行此操作 - 这段代码仅用于演示目的。

编码器函数定义为:Callable[['Encoder', Type[K], K], Union[Any, UnsupportedType]] 每个自定义编码器都有一个 encoder 参数,它持有 Encoder 实例。这对于编码嵌套类型,如列表或类等非常有用。

自定义解码器

typ.json.loadtyp.json.loads 函数接受解码器列表作为参数。与 编码 类似,它对于自定义解码逻辑非常有用。以下是一个解码 int 类型的 JSON 字符串的镜像示例

from typ.encoding import Unsupported, check_type

def decode_int_custom(decoder, typ, json_value):
    if typ != int:
        # if this encoder is not applicable to the typ it should return Unsupported
        return Unsupported
    # check that JSON has string in the json_value
    check_type(str, json_value)
    # return decoded value
    return int(json_value)

from typ import json
assert loads(List[int], '["3", "4", "5"]', decoders=[decode_int_custom]) == [3, 4, 5]

解码器函数定义为:Callable[['Decoder', Type[K], Any], Union[K, UnsupportedType]]

API 参考文档

typ.json.dumps

typ.json.dumps(value: T, typ: Optional[Type[T]] = None, case: CaseConverter = None, encoders: List[EncodeFunc] = [], indent: Optional[int] = None) -> str

使用指定的类型将值序列化为 JSON 格式的字符串。

value 要序列化为 JSON 的 Python 对象。

typ value 的类型信息。如果提供 None,则使用 value 的实际类型;否则,检查 value 是否是 typ 的有效实例。

case 案例转换器,请参阅 字段案例

encoders 自定义编码器列表,请参阅 自定义编码

indent JSON 的可选非负缩进级别。如果提供 None,则 JSON 将表示为单行,没有缩进。

返回 JSON 字符串或抛出 JsonError

typ.json.dump

typ.json.dump(fp: IO[str], value: T, typ: Optional[Type[T]] = None, case: CaseConverter = None, encoders: List[EncodeFunc] = [], indent: Optional[int] = None) -> None

将值序列化为 JSON 格式的流。

fp 写入 JSON 的流。

其他参数与 typ.json.dumps 中的相同。

typ.json.loads

typ.json.loads(typ: Type[T], json_str: str, case: CaseConverter = None, decoders: List[DecodeFunc] = []) -> T

将 json_str 反序列化为指定类型的 Python 对象。

typ 反序列化 JSON 到的类型。

json_str 包含 JSON 的字符串。

case 案例转换器,请参阅 字段案例

decoders 自定义解码器列表,请参阅 自定义编码

返回 M 的实例或抛出 JsonError

typ.json.load

typ.json.load(fp: IO[str], typ: Type[T], case: CaseConverter = None, decoders: List[DecodeFunc] = []) -> T

将流反序列化为指定类型的 Python 对象。

fp 从中读取 JSON 的流。

其他参数与 typ.json.loads 中的相同。

typ.json.JsonError (定义为 typ.encoding.JsonError)

JsonError 在根据提供的类型信息编码/解码 JSON 数据时引发。

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。

源代码分发

typjson-0.0.32.tar.gz (12.8 kB 查看哈希值)

上传时间 源代码

构建分发

typjson-0.0.32-py3-none-any.whl (9.5 kB 查看哈希值)

上传于 Python 3

支持者