一个使反序列化变得简单的库。
项目描述
反序列化
一个使反序列化变得简单的库。要开始使用,只需运行 pip install deserialize
过去的样子
在没有库的情况下,如果你想将
{
"a": 1,
"b": 2
}
转换为专用类,你必须做类似这样的事情
class MyThing:
def __init__(self, a, b):
self.a = a
self.b = b
@staticmethod
def from_json(json_data):
a_value = json_data.get("a")
b_value = json_data.get("b")
if a_value is None:
raise Exception("'a' was None")
elif b_value is None:
raise Exception("'b' was None")
elif type(a_value) != int:
raise Exception("'a' was not an int")
elif type(b_value) != int:
raise Exception("'b' was not an int")
return MyThing(a_value, b_value)
my_instance = MyThing.from_json(json_data)
现在的样子
使用 反序列化
,你所需要做的就是这样做
import deserialize
class MyThing:
a: int
b: int
my_instance = deserialize.deserialize(MyThing, json_data)
就是这样。它将提取所有数据并将其设置为你类型检查,甚至检查空值。
如果你想允许空值,那也很简单
from typing import Optional
class MyThing:
a: Optional[int]
b: Optional[int]
现在 None
是这些的有效值。
类型可以嵌套到你想要的深度。例如,这是完全有效的
class Actor:
name: str
age: int
class Episode:
title: str
identifier: st
actors: List[Actor]
class Season:
episodes: List[Episode]
completed: bool
class TVShow:
seasons: List[Season]
creator: str
高级用法
自定义键
可能你想要给你的对象属性命名与数据中的不同。这可能是因为可读性原因,或者因为你必须这样做(例如,如果你的数据项被命名为 __class__
)。这也同样可以处理。只需使用如下所示的 key
注解
@deserialize.key("identifier", "id")
class MyClass:
value: int
identifier: str
现在将具有键 id
的数据分配给字段 identifier
。您可以使用多个注释来覆盖多个键。
自动蛇形命名
数据通常会以驼峰式或大驼峰式命名。由于 Python 使用 snake_case 作为成员的标准,这意味着通常会使用自定义键来进行转换。为了使这个过程更简单,您可以添加 auto_snake
装饰器,它会在可能的情况下为您执行此转换。
@deserialize.auto_snake()
class MyClass:
some_integer: int
some_string: str
现在您可以传递这些数据,它将自动解析
{
"SomeInteger": 3,
"SomeString": "Hello"
}
注意,如果您使用此装饰器,所有字段都需要是蛇形命名。
未处理的字段
通常,如果您在定义中没有指定字段,但它在数据中存在,您只想忽略它。有时,您可能希望知道是否存在额外数据。在这种情况下,当调用 deserialize(...)
时,您可以将 throw_on_unhandled=True
设置为 True,并且如果数据中存在未处理的字段,它将引发异常。
此外,有时您可能需要这样做,但知道某个特定字段可以忽略。您可以使用装饰器 @allow_unhandled("key_name")
将这些标记为允许未处理的。
忽略的键
您可能希望对象中的一些属性不是从磁盘加载,而是以其他方式创建。为此,请使用 ignore
装饰器。以下是一个示例
@deserialize.ignore("identifier")
class MyClass:
value: int
identifier: str
在反序列化时,库现在将忽略 identifier
属性。
解析器
有时您希望对象中的某些内容以数据不是的格式存在。例如,如果您获得的数据
{
"successful": True,
"timestamp": 1543770752
}
您可能希望它表示为
class Result:
successful: bool
timestamp: datetime.datetime
默认情况下,它将因数据中的值不是时间戳而失败。为了纠正这一点,请使用 parser
装饰器来告诉它一个用于解析数据的功能。例如
@deserialize.parser("timestamp", datetime.datetime.fromtimestamp)
class Result:
successful: bool
timestamp: datetime.datetime
现在将在处理数据时检测到 key timestamp
,并运行它通过提供的解析器函数,然后再将其分配给新的类实例。
解析器是在类型检查之前运行的。这意味着如果您有类似 Optional[datetime.datetime]
的内容,您应该确保您的解析器可以处理值为 None
的情况。您的解析器显然需要返回您在属性中声明的类型才能正常工作。
子类化
支持子类化。例如,如果您有一个类型 Shape
,它有一个子类 Rectangle
,那么当您尝试将数据解码为 `rectangle` 对象时,对 Shape
上的任何属性都提供支持。
原始存储
有时保留用于构建对象的原始数据引用可能很有用。为此,只需将 raw_storage_mode
参数设置为 RawStorageMode.ROOT
或 RawStorageMode.ALL
。这将分别在根对象上或在树中的所有对象上存储数据,参数名为 __deserialize_raw__
。
默认值
有些数据到达时会缺少字段。在这些情况下,通常已知默认值。为此,只需按如下方式装饰您的类
@deserialize.default("value", 0)
class IntResult:
successful: bool
value: int
如果您传入类似 {"successful": True}
的数据,则将 value
的反序列化默认值设置为 0
。注意,这不会反序列化,因为 value
不是可选的:{"successful": True, "value": None}
。
后处理
不是所有内容都可以立即设置在您的数据上。有些事情需要在之后解决。为此,您需要进行一些后处理。最简单的方法是通过 @constructed
装饰器。此装饰器接受一个函数,该函数将在用该实例作为参数构造新实例时调用。以下是一个示例,该示例将极坐标从使用度转换为弧度
data = {
"angle": 180.0,
"magnitude": 42.0
}
def convert_to_radians(instance):
instance.angle = instance.angle * math.pi / 180
@deserialize.constructed(convert_to_radians)
class PolarCoordinate:
angle: float
magnitude: float
pc = deserialize.deserialize(PolarCoordinate, data)
print(pc.angle, pc.magnitude)
>>> 3.141592653589793 42.0
向下转型
数据通常会以具有类型字段的形式出现。这可能很难解析。例如
data = [
{
"data_type": "foo",
"foo_prop": "Hello World",
},
{
"data_type": "bar",
"bar_prop": "Goodbye World",
}
]
由于两个字段之间不同,没有一种好的方法来解析这些数据。您可以在某些基类上使用可选字段,尝试多次反序列化直到找到正确的方法,或者根据您构建的 data_type
字段映射来执行反序列化。然而,这些方案都不够优雅,如果类型嵌套,所有方案都有问题。相反,您可以使用 downcast_field
和 downcast_identifier
装饰器。
downcast_field
在基类上指定,并给出了包含类型信息的字段的名称。downcast_identifier
接受一个基类和一个标识符(这应该是基类中 downcast_field
的可能值之一)。内部,当一个具有下转换字段的类被检测到时,该字段将被提取,并搜索具有匹配标识符的子类。如果不存在此类,将抛出 UndefinedDowncastException
。
以下是一个处理上述数据的示例
@deserialize.downcast_field("data_type")
class MyBase:
type_name: str
@deserialize.downcast_identifier(MyBase, "foo")
class Foo(MyBase):
foo_prop: str
@deserialize.downcast_identifier(MyBase, "bar")
class Bar(MyBase):
bar_prop: str
result = deserialize.deserialize(List[MyBase], data)
在此,result[0]
将是一个 Foo
的实例,而 result[1]
将是一个 Bar
的实例。
如果您不能描述所有类型,您可以在您的基类上使用 @deserialize.allow_downcast_fallback
,并将未知内容保留为字典。
项目详情
下载文件
下载您平台上的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。