为了将状态传递给反序列化,您应该使用DeserializeSeed https://docs.rs/serde/latest/serde/de/trait.DeserializeSeed.html特征。的文档DeserializeSeed
解决这个用例:
DeserializeSeed
是有状态的形式Deserialize
特征。如果您发现自己正在寻找一种将数据传递到Deserialize
暗示,这个特质就是做到这一点的方法。
有状态的AssetLoader
就像你说的,路过AssetLoader
作为通用参数意味着您无法在其中存储缓存(或其他内容)。使用DeserializeSeed
,我们可以传递我们的一个实例AssetLoader
结构体,所以我们修改一下AssetLoader
的函数来授予访问权限self
:
pub trait AssetLoader {
// Adding `&mut self` allows implementers to store data in a cache or
// whatever else they want to do.
fn load_image(&mut self, path: &str) -> Image;
}
现在我们可以修改AssetLoaderImpl
使用这个新定义:
struct AssetLoaderImpl {
// cache, etc.
}
impl AssetLoader for AssetLoaderImpl {
fn load_image(&mut self, path: &str) -> Image {
// Access cache here.
println!("Loading image: {}", path);
Image {}
}
}
反序列化AssetLoader
现在我们可以使用AssetLoader
在反序列化期间使用DeserializeSeed
特征。因为我们希望这适用于任何实施者AssetLoader
(允许我们将文件系统逻辑与反序列化逻辑分开),我们仍然必须使用通用的L: AssetLoader
,但它不再需要附加到Texture
结构体(或任何包含Texture
).
一个好的模式是引入一个单独的TextureDeserializer
类型来处理有状态反序列化,并实现DeserializeSeed
在那个结构上。我们可以设置Value
关联类型指示反序列化应返回Texture
.
pub struct Texture {
path: String,
image: Image,
}
struct TextureDeserializer<'a, L> {
asset_loader: &'a mut L,
}
impl<'de, L> DeserializeSeed<'de> for TextureDeserializer<'_, L>
where
L: AssetLoader,
{
type Value = Texture;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
let path = String::deserialize(deserializer)?;
let image = self.asset_loader.load_image(&path);
Ok(Texture { path, image })
}
}
请注意,通用AssetLoader
不再由`Texture 直接使用。
我们现在必须定义DeserializeSeed
一直到链条上Scene
的反序列化逻辑,因为我们将有AssetLoader
状态贯穿整个过程。这可能看起来非常冗长,不幸的是我们不能直接用serde-derive
,但是不将反序列化状态绑定在我们正在反序列化的结构中的优点远远超过了额外的冗长。
反序列化一个Vec<Texture>
,我们定义一个TexturesDeserializer
:
struct TexturesDeserializer<'a, L> {
asset_loader: &'a mut L,
}
impl<'de, L> DeserializeSeed<'de> for TexturesDeserializer<'_, L>
where
L: AssetLoader,
{
type Value = Vec<Texture>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
struct TexturesVisitor<'a, L> {
asset_loader: &'a mut L,
}
impl<'de, L> Visitor<'de> for TexturesVisitor<'_, L>
where
L: AssetLoader,
{
type Value = Vec<Texture>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a sequence of Textures")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut textures = Vec::new();
while let Some(texture) = seq.next_element_seed(TextureDeserializer {
asset_loader: self.asset_loader,
})? {
textures.push(texture);
}
Ok(textures)
}
}
deserializer.deserialize_seq(TexturesVisitor {
asset_loader: self.asset_loader,
})
}
}
And a SceneDeserializer
反序列化Scene
itself:
pub struct Scene {
textures: Vec<Texture>,
}
pub struct SceneDeserializer<'a, L> {
pub asset_loader: &'a mut L,
}
impl<'de, L> DeserializeSeed<'de> for SceneDeserializer<'_, L>
where
L: AssetLoader,
{
type Value = Scene;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
struct SceneVisitor<'a, L> {
asset_loader: &'a mut L,
}
impl<'de, L> Visitor<'de> for SceneVisitor<'_, L>
where
L: AssetLoader,
{
type Value = Scene;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct Scene")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
if let Some(key) = map.next_key()? {
if key != "textures" {
return Err(de::Error::unknown_field(key, FIELDS));
}
} else {
return Err(de::Error::missing_field("textures"));
}
let textures = map.next_value_seed(TexturesDeserializer {
asset_loader: self.asset_loader,
})?;
Ok(Scene { textures })
}
}
const FIELDS: &[&str] = &["textures"];
deserializer.deserialize_struct(
"Scene",
FIELDS,
SceneVisitor {
asset_loader: self.asset_loader,
},
)
}
}
注意上面这些DeserializeSeed
定义与生成的非常相似#[derive(Deserialize)]
(如果是Scene
)以及已经定义的内容serde
for Vec<T>
。然而,定义这些自定义实现允许状态通过整个过程传递到反序列化中Texture
.
把它们放在一起
现在我们可以使用serde_json
从我们的 JSON 输入反序列化。注意serde_json
不提供任何反序列化的帮助方法DeserializeSeed
(已经有讨论 https://github.com/serde-rs/json/issues/282过去对此进行过),所以我们必须使用serde_json::Deserializer
手动。对我们来说幸运的是,它的使用非常简单:
fn main() {
let mut asset_loader = AssetLoaderImpl {
// cache, etc.
};
let scene_str = r#"
{
"textures": [
"texture1.jpg",
"texture2.jpg"
]
}
"#;
let mut deserializer = serde_json::Deserializer::new(serde_json::de::StrRead::new(&scene_str));
let scene = SceneDeserializer {
asset_loader: &mut asset_loader,
}.deserialize(&mut deserializer);
// ...
}
现在我们可以反序列化Scene
有状态的AssetLoader
。这可以很容易地扩展到包括其他成员的其他资源Scene
也可以在反序列化期间访问。最重要的是,它使反序列化状态与实际的反序列化结构分离,这意味着您不需要关心什么AssetLoader
在反序列化之外使用。