我的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>
实现Deserializer
和Vistor
由于我是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条件)...
2条答案
按热度按时间x0fgdtte1#
我认为解决这个问题的“经典”方法是反序列化到一个枚举中,对于每个要反序列化到的潜在“真实的”类型,都有一个变量。不幸的是,
And
是泛型的,这意味着这些泛型参数也必须存在于枚举中,因此必须在反序列化的地方指定它们。图纸
And(And { left: Equal { ref_val: "goofy" }, right: Equal { ref_val: "goofy" } })
ecr0jaav2#
我从combinator条件中删除了泛型,所以现在可以像@EvilTak建议的那样使用typetag:
(on缺点是,我必须删除派生宏PartialEq和Debug)
有趣的附带事实:我必须保留And Struct上的
#[serde(tag="type")]
,因为否则类型标记将在序列化中被省略(对于基本构造,不需要它)UPDATE:typetag只为trait对象添加type标记,因此不需要#[serde(tag="type")]
...