Rust serde反序列化动态特性

e0bqpujr  于 2023-02-12  发布在  其他
关注(0)|答案(2)|浏览(153)

我的pet-project中有一个递归数据结构:
(this是简化示例)

pub trait Condition {
        fn validate(&self, s: &str) -> bool;
}

pub struct Equal {
    ref_val: String,
}
impl Condition for Equal {
    fn validate(&self, s: &str) -> bool { self.ref_val == s }
}

pub struct And<A, B> where A: Condition + ?Sized, B: Condition + ?Sized {
    left: Box<A>,
    right: Box<B>,
}

impl<A, B> Condition for And<A, B> where A: Condition + ?Sized, B: Condition + ?Sized {
    fn validate(&self, s: &str) -> bool { self.left.validate(s) && self.right.validate(s) }
}

我想串行化和解串行化条件特征(使用serde),例如:

fn main() {

    let c = And {
        left: Box::new(Equal{ ref_val: "goofy".to_string() }),
        right: Box::new(Equal{ ref_val: "goofy".to_string() }),
    };

    let s = serde_json::to_string(&c).unwrap();

    let d: Box<dyn Condition> = serde_json::from_string(&s).unwrap();
}

因为serde不能反序列化dyn traits,所以我标记了序列化标记,例如:

#[derive(PartialEq, Debug, Serialize)]
#[serde(tag="type")]
pub struct Equal {
    ref_val: String,
}

并尝试为Box<dyn Condition>实现DeserializerVistor
由于我是Rust的新手,并且由于反序列化器和访问者的实现在给定的文档中不是那么简单,我想知道是否有人知道如何用更简单的方法解决我的问题?
我浏览了serde文档,并在技术网站/论坛上搜索了解决方案。我试用了typetag,但它不支持泛型类型
更新:
更准确地说:序列化工作正常,即serde可以序列化Condition特征的任何具体对象,但是为了反序列化一个条件,需要提供具体的类型信息。2但是这个类型信息在编译时是不可用的。3我正在写一个Web服务,客户可以在其中上传上下文匹配规则(即条件),以便Web服务的控制器在需要反序列化条件时不知道类型。例如,客户可以发布:

{"type":"Equal","ref_val":"goofy"}

{"type":"Greater","ref_val":"Pluto"}

或更复杂的任意组合符("and"、"or"、"not")

{"type":"And","left":{"type":"Greater","ref_val":"Gamma"},"right":{"type":"Equal","ref_val":"Delta"}}

因此我需要使用序列化标记中的type标记反序列化为trait(dyn条件)...

x0fgdtte

x0fgdtte1#

我认为解决这个问题的“经典”方法是反序列化到一个枚举中,对于每个要反序列化到的潜在“真实的”类型,都有一个变量。不幸的是,And是泛型的,这意味着这些泛型参数也必须存在于枚举中,因此必须在反序列化的地方指定它们。

use serde::{Deserialize, Serialize};
use serde_json; // 1.0.91 // 1.0.152

pub trait Condition {
    fn validate(&self, s: &str) -> bool;
}

#[derive(PartialEq, Debug, Serialize, Deserialize)]
pub struct Equal {
    ref_val: String,
}
impl Condition for Equal {
    fn validate(&self, s: &str) -> bool {
        self.ref_val == s
    }
}

#[derive(PartialEq, Debug, Serialize, Deserialize)]
pub struct And<A, B>
where
    A: Condition + ?Sized,
    B: Condition + ?Sized,
{
    left: Box<A>,
    right: Box<B>,
}

impl<A, B> Condition for And<A, B>
where
    A: Condition + ?Sized,
    B: Condition + ?Sized,
{
    fn validate(&self, s: &str) -> bool {
        self.left.validate(s) && self.right.validate(s)
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
enum Expr<A, B>
where
    A: Condition + ?Sized,
    B: Condition + ?Sized,
{
    Equal(Equal),
    And(And<A, B>),
}

fn main() {
    let c = And {
        left: Box::new(Equal {
            ref_val: "goofy".to_string(),
        }),
        right: Box::new(Equal {
            ref_val: "goofy".to_string(),
        }),
    };

    let s = serde_json::to_string(&c).unwrap();

    let d: Expr<Equal, Equal> = serde_json::from_str(&s).unwrap();
    println!("{d:?}");
}

图纸And(And { left: Equal { ref_val: "goofy" }, right: Equal { ref_val: "goofy" } })

ecr0jaav

ecr0jaav2#

我从combinator条件中删除了泛型,所以现在可以像@EvilTak建议的那样使用typetag

#[derive(Serialize, Deserialize)]
#[serde(tag="type")]
pub struct And {
    left: Box<dyn Condition>,
    right: Box<dyn Condition>,
}
#[typetag::serde]
impl Condition for And {
    fn validate(&self, s: &str) -> bool { self.left.validate(s) && self.right.validate(s) }
}

(on缺点是,我必须删除派生宏PartialEq和Debug)
有趣的附带事实:我必须保留And Struct上的#[serde(tag="type")],因为否则类型标记将在序列化中被省略(对于基本构造,不需要它)UPDATE:typetag只为trait对象添加type标记,因此不需要#[serde(tag="type")]...

相关问题