语境
我为 JSON Schema 验证实现了一个 Rust 库,它与serde_json::Value
实例一起运行。现在我想从 Python 中使用它并考虑PyO3
作为连接它们的主要选择。Python 值应该serde_json::Value
在传递到库时转换为 Python 值,并且serde_json::Value
应该在 Rust 部分返回的验证错误内转换回 Python。
一种可能的方法是实现serde::se::Serialize
一个新类型的包装器pyo3::types::PyAny
,然后将其传递给serde_json::to_value
,但我不确定它的效率如何。有哪些选择以及有哪些取舍?
在 Python 方面,我最感兴趣的是内置类型,它们可以由 序列化json.dumps
,目前没有自定义类。
生锈方面的例子:
use jsonschema::{JSONSchema, Draft, CompilationError};
use serde_json::Value;
pub fn is_valid(schema: &Value, instance: &Value) -> bool {
let compiled = JSONSchema::compile(schema, None).expect("Invalid schema");
compiled.is_valid(instance)
}
即有一个函数接受两个引用serde_json::Value
,我想将它公开给 Python。从 Python 方面来看,可能有两个用例:
- 该实例是一个 JSON 编码的字符串:
import jsonschema_rs
assert jsonschema_rs.is_valid(
{"minItems": 2},
"[1, 2]"
)
- 该实例是一个 Python 结构(不是 JSON 编码的字符串):
import jsonschema_rs
assert jsonschema_rs.is_valid(
{"minItems": 2},
[1, 2]
)
可能的用例
Web 应用程序请求/响应结构验证。
- 当一个请求进入时,它的主体被原样验证,而不根据模式进行解析。
- 返回响应时,在序列化为 JSON 之前,根据 schema 验证此结构;
将来,这两个步骤可能会与 Rust 支持的 JSON 反序列化(在请求端)和反序列化(在响应端)结合使用。
- 使用基于属性的测试作为假设的扩展
在这种情况下,输入验证越快,生成的测试用例就越多。当前的实现在底层使用 Python库,这对于我通常使用的复杂模式来说非常慢。
更新
我尝试在此处Serialize
实现trait并添加了与 @Sven Marnach 在评论中建议的原始字符串输入的比较。确实,原始字符串是最快的选择,但如果它涉及在 Python 中调用,它比具有特征的变体要差得多。json.dumps
小对象和模式(100000 次迭代):
String : 1.31617
Trait : 1.52797 (x1.16)
String + dumps: 2.77378 (x2.1)
大对象和模式(100 次迭代):
String : 1.42146
Trait : 3.70745 (x2.6)
String + dumps: 6.21213 (x4.37)
基准代码和测试数据。
有一个字符串版本肯定是有意义的,但调用json.dumps
是相当昂贵的。我不知道这种情况是否有更好的选择。
Python版本:3.7
锈版本:1.42.0
依赖项:
- serde_json = "1.0.48"
- serde =“1.0.105”
- jsonschema = "0.2.0"