2

语境

我为 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 方面来看,可能有两个用例:

  1. 该实例是一个 JSON 编码的字符串:
import jsonschema_rs

assert jsonschema_rs.is_valid(
    {"minItems": 2}, 
    "[1, 2]"
)
  1. 该实例是一个 Python 结构(不是 JSON 编码的字符串):
import jsonschema_rs

assert jsonschema_rs.is_valid(
    {"minItems": 2},
    [1, 2]
)

可能的用例

  1. Web 应用程序请求/响应结构验证。

    • 当一个请求进入时,它的主体被原样验证,而不根据模式进行解析。
    • 返回响应时,在序列化为 JSON 之前,根据 schema 验证此结构;

将来,这两个步骤可能会与 Rust 支持的 JSON 反序列化(在请求端)和反序列化(在响应端)结合使用。

  1. 使用基于属性的测试作为假设的扩展

在这种情况下,输入验证越快,生成的测试用例就越多。当前的实现在底层使用 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"
4

0 回答 0