如何有效地将Python结构转换为serde_json::Value?

2024-05-13 14:15:00 发布

您现在位置:Python中文网/ 问答频道 /正文

上下文

我实现了一个用于JSON模式验证的Rust库,该库使用serde_json::Value实例进行操作。现在我想从Python中使用它,并将PyO3作为连接它们的主要选择。Python值在传递到库时应转换为serde_json::Value,并且serde_json::Value应在Rust部件返回的验证错误中转换回Python

其中一种可能的方法是在pyo3::types::PyAny周围为新类型包装器实现serde::se::Serialize,然后将其传递给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)
}

也就是说,有一个function接受对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之前,根据模式验证该结构

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

  1. 用于基于属性的测试,作为Hypothesis的扩展

在这种情况下,验证输入的速度越快,生成的测试用例就越多。当前的implementation在后台使用Python library,这对于我通常使用的复杂模式来说相当慢

更新

我试图实现Serializetraithere,并添加了一个与原始字符串输入的比较,正如@Sven Marnach在注释中所建议的那样。事实上,原始字符串是最快的选项,但是如果它涉及在Python中调用json.dumps,那么它会比带有trait的变体糟糕得多

小物件及;模式(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)

基准code and test data

为字符串提供一个版本肯定是有意义的,但是调用json.dumps是相当昂贵的。我不知道在这种情况下是否有更好的选择

Python版本:3.7

防锈版本:1.42.0

依赖关系

  • serde_json=“1.0.48”
  • serde=“1.0.105”
  • jsonschema=“0.2.0”

Tags: 实例字符串jsonstring序列化isvalueschema