在我的程序开始时,我从一个文件中读取数据:
let file = std::fs::File::open("data/games.json").unwrap();
let data: Games = serde_json::from_reader(file).unwrap();
我想知道在编译时如何做到这一点,原因如下:
- 性能:无需在运行时反序列化
- 可移植性:该程序可以在任何机器上运行,不需要包含数据的 json 文件。
我可能还需要提一下,数据可以是只读的,这意味着解决方案可以将其存储为静态数据。
最佳答案
这很简单,但会导致一些潜在的问题。首先,我们需要处理一些事情:我们是想从文件加载对象树,还是在运行时解析它?
99% 的时间,在启动时解析为 static ref
对人们来说已经足够了,所以我将为您提供该解决方案;我会在最后向您指出“其他”版本,但这需要很多更多的工作并且是特定于域的。
您正在寻找能够在编译时包含文件的宏(因为它必须是宏)在标准库中:std::include_str!
.顾名思义,它会在编译时获取您的文件,并从中生成一个 &'static str
供您使用。然后您可以自由地使用它做任何您喜欢的事情(例如解析它)。
从那里开始,使用 lazy_static!
就很简单了为我们的 JSON Value
(或您决定使用的任何内容)生成一个 static ref
供程序的每个部分使用。例如,在您的情况下,它可能看起来像这样:
const GAME_JSON: &str = include_str!("my/file.json");
#[derive(Serialize, Deserialize, Debug)]
struct Game {
name: String,
}
lazy_static! {
static ref GAMES: Vec<Game> = serde_json::from_str(&GAME_JSON).unwrap();
}
执行此操作时需要注意两件事:
- 这将大量 使您的文件大小膨胀,因为
&str
没有以任何方式压缩。考虑 gzip - 您需要担心对同一
static ref
的多个线程访问的常见问题,但由于它不是可变的,您实际上只需要担心它的一部分
另一种方法需要在编译时使用过程宏动态生成对象。如前所述,除非您真的在解析该 JSON 时有真的昂贵的启动成本,否则我不会推荐它;大多数人不会,我上次遇到这种情况是在处理深度嵌套的多 GB JSON 文件时。
您要注意的 crate 是用于代码生成的 proc_macro2
和 syn
;其余部分与您编写普通方法的方式非常相似。
关于rust - 在编译时使用 serde_json 反序列化文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58359340/