提问人:lmseper 提问时间:6/17/2023 更新时间:6/17/2023 访问量:80
反序列化“dyn T”结构的 JSON 列表?
Deserialize JSON list of `dyn T` structs?
问:
我正在尝试学习 Rust,并且一直在构建一个光线追踪器作为初学者项目,以弄脏我的语言。我按照 Peter Shirley 的《One Weekend 中的光线追踪》构建了我的光线追踪器 这本书是用 C++ 编写的,我已经尽力将其移植到 Rust 中,诚然,代码可能不是以类似 Rust 的方式编写的。
我已经让光线追踪器工作了,我目前正在尝试实现一个场景保存系统,您可以在其中将场景保存为 JSON 文件,其中包含有关场景相机、对象等的各种信息。
在使用反序列化场景的 JSON 以便将其加载到程序中时,我遇到了一个问题。我在一个简单的 Python 脚本中实现了,其输出如下所示,保存为:serde
scene.json
{
"aspect_ratio": 1.5,
"image_width": 1200,
"image_height": 800,
"samples_per_pixel": 500,
"camera": {
"origin": {
"vec": [
13.0,
2.0,
3.0
]
},
"lower_left_corner": {
"vec": [
3.0112371,
-1.1992972,
2.6938112
]
},
"horizontal": {
"vec": [
1.5556582,
0,
-5.055889
]
},
"vertical": {
"vec": [
-0.49034908,
3.4890225,
-0.15087664
]
},
"u": {
"vec": [
0.29408586,
0,
-0.955779
]
},
"v": {
"vec": [
-0.13904539,
0.9893614,
-0.042783197
]
},
"w": {
"vec": [
0.9456108,
0.14547859,
0.29095718
]
},
"radius": 0.5
},
"world": [
{
"center": {
"vec": [
0,
-1000,
0
]
},
"r": 1000,
"material": {
"type": "lambertian",
"albedo": [
0.5,
0.5,
0.5
]
}
},
{
"center": {
"vec": [
0.3712137,
0.2,
1.5109301
]
},
"r": 0.2,
"material": {
"type": "metal",
"albedo": [
0.66978514,
0.9735459,
0.52093863
],
"fuzziness": 0.045185298
}
},
.
.
.
]
}
目前我的光线追踪器中只有球体,所以两个 s 之间唯一不同的是 ,我有 、 和,并定义如下:Renderable
"world"
"material"
LambertianMaterial
Metal
Dielectric
pub struct LambertianMaterial {
albedo: Color
}
pub struct Metal {
albedo: Color,
fuzziness: f32
}
pub struct Dielectric {
// index of refraction (𝜂')
ir: f32
}
对元数据(如 和 、 等)进行去整理是完全可以的。我的问题是这个字段,因为它是一个列表,其中包含所有使用该特征的各种领域,我将其定义为:"camera"
"aspect_ratio"
"image_width
"world"
Renderable
pub trait Renderable: fmt::Display {
fn hit (&self, ray: &Ray, t_min: f32, t_max: f32) -> (bool, HitRecord);
}
在光线追踪器中,这些球体通过循环访问另一个包含 s 的结构体来渲染,称为 ,定义为:vec
Renderable
RenderableList
pub struct RenderableList {
objects: Vec<Rc<dyn Renderable>>
}
然而,尝试使用 serde 从 JSON 反序列化 a 被证明是困难的。简单地在结构定义上打一个显然并不能解决它。RenderableList
#[derive(Debug, Deserialize)]
我尝试通读这篇关于序列化 Arc<Mutex> 的文章,但它更多的是在序列化方面,而不是反序列化方面。
我尝试通读有关实现反序列化器的 serde 文档,但我认为这比我实际需要的更复杂和矫枉过正。
我最初的想法是做以下事情:
#[derive(Deserialize, Debug)]
struct Scene {
aspect_ratio: f32,
image_width: i32,
image_height: i32,
samples_per_pixel: i32,
camera: Camera,
world: RenderableList,
}
fn load_scene<P: AsRef<Path>>(path: P) -> Result<Scene, Box<dyn Error>> {
// Open the file in read-only mode with buffer.
let file = File::open(path)?;
let reader = BufReader::new(file);
// Read the JSON contents of the file as an instance of `Scene`.
let s = serde_json::from_reader(reader)?;
// Return the `Scene`.
Ok(s)
}
但如前所述,当尝试使用 进行反序列化时,这会产生错误。具体来说,我得到:RenderableList
#[derive(Debug, Deserialize)]
the trait bound `Rc<dyn Renderable>: Deserialize<'_>` is not satisfied
the following other types implement trait `Deserialize<'de>`:
&'a Path
&'a [u8]
&'a str
()
(T0, T1)
(T0, T1, T2)
(T0, T1, T2, T3)
(T0, T1, T2, T3, T4)
and 131 others
required for `Vec<Rc<dyn Renderable>>` to implement `Deserialize<'_>`
有人碰巧知道如何解决这个问题吗?
答:
解决此问题的最直接方法是使用枚举而不是特征对象。
#[derive(Debug, Deserialize)]
#[serde(untagged)] // since your current JSON is untagged
pub enum Object {
Circle(Circle),
}
#[derive(Debug, Deserialize)]
pub struct RenderableList {
objects: Vec<Rc<Object>>
}
impl Renderable for Object {
fn hit (&self, ray: &Ray, t_min: f32, t_max: f32) -> (bool, HitRecord) {
match self {
Object::Circle(c) => c.hit(ray, t_min, t_max)
}
}
}
现在,当您添加新对象时,您将:
- 为该对象创建一个类型。
- 将多属性添加到
Object
- 为对象实现。
Renderable
- 向匹配表达式添加分支。
您可以对材料执行相同的操作。
实际上没有一种方法可以检索特征的所有实现者的列表,您需要这样做才能反序列化特征对象。也没有内置的方法来检索枚举变体列表,但 serde 派生宏可以处理它。枚举通常也比特征对象快。
我确实找到了这个可以对特征对象执行此操作的板条箱,但我还没有尝试过:typetag。
另请参阅有关枚举的 serde 指南章节。
评论