处理JSON作为Python对象的库
项目描述
jsonobject
jsonobject是一个Python库,用于处理深度嵌套的JSON对象,这些对象具有良好的Python对象模式。
jsonobject由Dimagi制作,我们在这里构建、使用并贡献开源软件,以减少世界上的不平等。
jsonobject受couchdbkit
中的Document
/DocumentSchema
部分启发,并与它高度兼容API。由于jsonobject不仅更简单且独立,而且更快,我们还维护了couchdbkit
的分支,jsonobject-couchdbkit,该分支由jsonobject支持,可以作为主库的无缝替代品。
它在CommCare HQ(源代码)中得到广泛使用,API基本稳定,但更高级的功能可能会在未来更改。
入门指南
要使用pip安装,只需运行
pip install jsonobject
示例
以下代码定义了一个简单的用户模型,以及其自然映射到JSON的方式。
from jsonobject import *
class User(JsonObject):
username = StringProperty()
name = StringProperty()
active = BooleanProperty(default=False)
date_joined = DateTimeProperty()
tags = ListProperty(unicode)
一旦定义,就可以用来包装或生成反序列化的JSON。
>>> user1 = User(
name='John Doe',
username='jdoe',
date_joined=datetime.datetime.utcnow(),
tags=['generic', 'anonymous']
)
>>> user1.to_json()
{
'name': u'John Doe',
'username': u'jdoe',
'active': False,
'date_joined': '2013-08-05T02:46:58Z',
'tags': [u'generic', u'anonymous']
}
注意,在JSON中,日期时间被转换为ISO格式的字符串,但在对象上是真实的日期时间。
>>> user1.date_joined
datetime.datetime(2013, 8, 5, 2, 46, 58)
JsonObject构造函数
上面定义的JsonObject子类User
具有许多内置功能。基本操作包括
- 从反序列化的JSON(例如
json.loads
的输出)创建新对象 - 使用给定值构造新对象
- 修改对象
- 转换为反序列化的JSON(例如
json.dumps
的输入)
1和2是通过构造函数实现的。调用构造函数主要有两种方式
User(
name='John Doe',
username='jdoe',
date_joined=datetime.datetime.utcnow(),
tags=['generic', 'anonymous']
)
如上(满足#2)和
User({
'name': u'John Doe',
'username': u'jdoe',
'active': False,
'date_joined': '2013-08-05T02:46:58Z',
'tags': [u'generic', u'anonymous']
})
(满足#1)。这两种样式也可以混合使用
User({
'name': u'John Doe',
'username': u'jdoe',
'active': False,
'tags': [u'generic', u'anonymous']
}, date_joined=datetime.datetime.utcnow())
注意,在反序列化的JSON中,日期时间以字符串的形式存储,但在漂亮的Python对象中是datetime.datetime
——我们将这些称为“json”表示和“python”表示,或者换句话说,“未展开”表示和“展开”表示。
注意。在调用构造函数时,请记住,关键字参数风格需要您传递“python”表示(例如一个datetime
),而传递dict
的json包装风格需要您以“json”表示(例如一个格式化的日期时间字符串)。
属性类型
主要有两种属性类型:标量类型(如字符串、布尔值、整数、日期时间等)和容器类型(列表、字典、集合)。以下分别介绍。
标量类型
除了它们各自类型特定的值(字符串、布尔值等)之外,所有标量属性还可以取None
值。如果设置错误类型,属性会引发jsonobject.exceptions.BadValueError
class Foo(jsonobject.JsonObject):
b = jsonobject.BooleanProperty()
>>> Foo(b=0)
Traceback (most recent call last):
[...]
jsonobject.exceptions.BadValueError: 0 not of type <type 'bool'>
jsonobject.StringProperty
映射到unicode
。用法
class Foo(jsonobject.JsonObject):
s = jsonobject.StringProperty()
如果您将其设置为ASCII str
,它将隐式转换为unicode
>>> Foo(s='hi') # converts to unicode
Foo(s=u'hi')
如果您将其设置为非ASCII str
,它将引发UnicodeDecodeError
>>> Foo(s='\xff')
Traceback (most recent call last):
[...]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 0: ordinal not in range(128)
jsonobject.BooleanProperty
映射到bool
。
jsonobject.IntegerProperty
映射到int
或long
。
jsonobject.FloatProperty
映射到float
。
jsonobject.DecimalProperty
映射到decimal.Decimal
并存储为JSON字符串。与FloatProperty
不同,这种类型存储数字的“人类”表示。用法
class Foo(jsonobject.JsonObject):
number = jsonobject.DecimalProperty()
如果您将其设置为int
或float
,它将隐式转换为Decimal
>>> Foo(number=1)
Foo(number=Decimal('1'))
>>> Foo(number=1.2)
Foo(number=Decimal('1.2'))
但是,如果您将其设置为str
或unicode
,则会引发AssertionError
>>> Foo(number='1.0')
Traceback (most recent call last):
[...]
AssertionError
待办事项:这应该真正引发一个BadValueError
。
如果您传递的JSON中Decimal值是一个str
或unicode
,但格式不正确,它将抛出与decimal.Decimal
相同的错误。
>>> Foo({'number': '1.0'})
Foo(number=Decimal('1.0'))
>>> Foo({'number': '1.0.0'})
Traceback (most recent call last):
[...]
decimal.InvalidOperation: Invalid literal for Decimal: '1.0.0'
jsonobject.DateProperty
映射到datetime.date
并以格式为'%Y-%m-%d'
的JSON字符串存储。用法
class Foo(jsonobject.JsonObject):
date = jsonobject.DateProperty()
包装格式错误的字符串会引发BadValueError
>>> Foo({'date': 'foo'})
Traceback (most recent call last):
[...]
jsonobject.exceptions.BadValueError: 'foo' is not a date-formatted string
jsonobject.DateTimeProperty
映射到无时区datetime.datetime
并以格式为'%Y-%m-%dT%H:%M:%SZ'
的JSON字符串存储。
尽管它对良好输入处理得很好,但在处理不符合指定格式的输入时却非常随意。它不会严格匹配,而是将字符串截断为前19个字符,并尝试将其解析为'%Y-%m-%dT%H:%M:%S'
。这忽略了微秒,甚至更糟糕的是,忽略了时区。这是从couchdbkit
继承下来的。
在jsonboject的新版本中,您可以选择指定一个DateTimeProperty
为exact
class Foo(jsonobject.JsonObject):
date = jsonobject.DateTimeProperty(exact=True)
这提供了一个更干净的转换模型,具有以下属性
- 它保留了微秒
- 传入的JSON表示必须与
'%Y-%m-%dT%H:%M:%S.%fZ'
完全匹配。(这与默认输出类似,但必须有6位小数,即毫秒。) - 与精确匹配不符的表示将被拒绝,并抛出
BadValueError
错误。
建议:如果您没有受到couchdbkit
早期不良行为的限制,您应该始终在DateTimeProperty
和TimeProperty
(以下)上使用exact=True
标志。
jsonobject.TimeProperty
映射到datetime.time
,以'%H:%M:%S'
格式的JSON字符串形式存储。
要访问毫秒并使用严格行为,请使用exact=True
设置,该设置严格接受格式'%H:%M:%S.%f'
。这总是推荐的做法。有关更多信息,请参阅上一节关于DateTimeProperty
的内容。
容器类型
容器类型通常需要一个参数,即item_type
,用于指定包含对象的类型。
jsonobject.ObjectProperty(item_type)
映射到由item_type
指定的模式的dict
,item_type
必须是其自身的JsonObject
子类。用法
class Bar(jsonobject.JsonObject):
name = jsonobject.StringProperty()
class Foo(jsonobject.JsonObject):
bar = jsonobject.ObjectProperty(Bar)
如果未指定,它将被设置为具有默认值的新的对象。
>>> Foo()
Foo(bar=Bar(name=None))
如果您想将其设置为None
,则必须显式地这样做。
jsonobject.ListProperty(item_type)
映射到类型为item_type
的list
,item_type
可以是以下任何一种
- 属性类的实例。这是最灵活的选项,所有验证(如
required
等)都将根据属性实例指定的方式进行。 - 属性类,它将以
required=True
的方式实例化 - 它们对应的Python类型之一(例如,
int
对应于IntegerProperty
等) JsonObject
的子类
请注意,属性类(以及相关的Python类型语法)将以required=True
的方式实例化,因此ListProperty(IntegerProperty)
和ListProperty(int)
不允许None
,而ListProperty(IntegerProperty())
则允许None
。
给定任何项目类型的序列化行为将递归地应用于列表中的每个成员。
如果未指定,它将被设置为空列表。
jsonobject.SetProperty(item_type)
映射到set
,并以列表的形式存储(仅包含唯一元素)。否则,它的行为与ListProperty
非常相似。
jsonobject.DictProperty(item_type)
映射到具有字符串键和由item_type
指定的值的dict
。否则,它的行为与ListProperty
非常相似。
如果未指定,它将被设置为空字典。
其他
jsonobject.DefaultProperty()
这灵活地包装了任何有效的JSON,包括所有标量和容器类型,动态检测值的类型并使用相应的属性对其进行处理。
属性选项
可以将某些参数传递给任何属性。
例如,下面的例子中的required
就是这样一个参数
class User(JsonObject):
username = StringProperty(required=True)
以下是属性列表的完整列表
-
default
指定属性的默认值
-
name
JSON表示中属性的名称。默认情况下,它将默认为Python属性的名称,但您可以在需要时覆盖它。这很有用,例如,可以绕过与Python关键字的冲突
>>> class Route(JsonObject): ... from_ = StringProperty(name='from') ... to = StringProperty() # name='to' by default >>> Route(from_='me', to='you').to_json() {'from': u'me', 'to': u'you'}
请注意,在Python属性名称('from_')中存在下划线,但在JSON属性名称('from')中不存在。
**如果您想知道为什么在上述例子中`StringProperty`的`name`参数可能默认为`to`,当它没有在初始化时访问`Route`类的属性时,您是完全正确的。描述的行为是在`JsonObject`的`__metaclass__`中实现的,它确实具有访问`Route`类属性的能力。 -
choices
属性的允许值列表。(除非另有说明,否则也允许
None
) -
required
默认为
False
。对于标量属性,requires
意味着不能使用值None
。对于容器属性,意味着它们不能为空或取值None
。 -
exclude_if_none
默认为
False
。当设置为true时,当该属性的值为falsey时,此属性将不包括在JSON输出中。(请注意,目前这与其参数名称相矛盾,因为条件是它为falsey,而不是它是None
)。 -
validators
单个验证函数或验证函数列表。每个验证函数在无效输入时应抛出异常,否则不执行任何操作。
-
verbose_name
此属性不执行任何操作,仅为了与couchdbkit的API兼容而添加。
与Couchdbkit的性能比较
为了与couchdbkit进行直接比较,测试套件包括一个大型样本模式,该模式最初是用couchdbkit编写的。很容易将jsonobject替换为couchdbkit,并分别运行测试。以下是结果:
$ python -m unittest test.test_couchdbkit
....
----------------------------------------------------------------------
Ran 4 tests in 1.403s
OK
$ python -m unittest test.test_couchdbkit
....
----------------------------------------------------------------------
Ran 4 tests in 0.153s
OK
开发生命周期
jsonobject
版本遵循语义版本控制。版本信息可在CHANGES.md中找到。
开发者和维护者信息,例如如何运行测试和发布新版本,可在LIFECYCLE.md中找到。
项目详情
jsonobject-2.2.0.tar.gz 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 519b9ba3fda5b60c218d669eb67c20d08b58706e7b9497c96a8089206ee48976 |
|
MD5 | a62f49e00eb9793bf4fa112b32bc768f |
|
BLAKE2b-256 | 681b315c6f09f6db3c2e7c1b64350fd0a31241af497bb1332833510afce3778d |
jsonobject-2.2.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 4d0f845e781684e2b571e8c52d44e5e522b2912393d87a3b61b928ebfc55bf64 |
|
MD5 | fda2e1306535951a999db2fe8f1a6abf |
|
BLAKE2b-256 | 1bd6ccde7e2e7e663dd90d369ec2cf718c94bb358cc547636c700b6e369e0a7f |