在编译时使用serde_json反序列化文件

时间:2019-10-12 23:13:05

标签: rust deserialization json-deserialization serde

在程序开始时,我从文件中读取数据:

let file = std::fs::File::open("data/games.json").unwrap();
let data: Games = serde_json::from_reader(file).unwrap();

由于以下原因,我想知道如何在编译时执行此操作:

  1. 性能:无需在运行时反序列化
  2. 可移植性:该程序可以在任何计算机上运行,​​而无需包含包含数据的json文件。

我可能还会提到,数据只能读取,这意味着解决方案可以将其存储为静态数据。

2 个答案:

答案 0 :(得分:1)

这很简单,但是会导致一些潜在的问题。首先,我们需要处理一些事情:我们要从文件中加载对象树还是在运行时进行解析?

99%的时间,在启动时解析为static ref对人们来说就足够了,因此,我将为您提供解决方案;我将在最后指向您指向“其他”版本,但这需要更多 lot 且针对特定领域。

您要能够在编译时包含文件的宏(因为它必须是宏)位于标准库std::include_str!中。顾名思义,它在编译时获取文件,并从中生成一个&'static str供您使用。然后,您可以随意使用它进行任何操作(例如解析)。

从那里开始,然后使用lazy_static!为我们的JSON static ref(或您决定去的任何东西)生成Value到我们的JSON 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

在执行此操作时,您需要注意两件事:

  1. 这将大量膨胀文件大小,因为static ref不会以任何方式压缩。考虑使用gzip
  2. 您需要担心与对同一proc_macro2的多个线程访问有关的常见问题,但是由于它是不可变的,因此您只需要担心其中的一部分

另一种方法要求在编译时使用过程宏动态生成对象。如前所述,除非您在解析该JSON时真正花费了昂贵的启动成本,否则我不建议您这样做。大多数人不会,而我上一次遇到这个是在处理深度嵌套的多GB JSON文件时。

您要查找的板条箱是syn public void sort() { library.sort(new BookComparator()); } 用于代码生成;其余的与您编写普通方法的方式非常相似。

答案 1 :(得分:0)

在运行时反序列化某些内容时,实际上是从磁盘上的另一种表示形式在程序存储器中构建某种表示形式。但是在编译时,还没有“程序存储器”的概念-这些数据也会在哪里反序列化?

但是,实际上,您想要实现的目标是可能的。主要思想如下:要在程序存储器中创建内容,必须编写一些代码来创建数据。如果您能够根据序列化的数据自动生成代码怎么办?这就是uneval板条箱所做的事情(免责声明:我是作者,因此建议您仔细阅读源代码,看看是否可以做得更好)。

要使用此方法,您必须创建具有以下内容的build.rs

// somehow include the Games struct with its Serialize and Deserialize implementations
fn main() {
    let games: Games = serde_json::from_str(include_str!("data/games.json")).unwrap();
    uneval::to_out_dir(games, "games.rs");
}

在初始化代码中,您将具有以下内容:

let data: Games = include!(concat!(env!("OUT_DIR"), "/games.rs"));

但是,请注意,按照人体工程学的方式进行此操作可能相当困难,因为现在必须在build.rs和板条箱本身之间共享必要的结构定义,正如我在评论中提到的那样。如果将板条箱一分为二,将结构定义(仅它们)保存在一个板条箱中,然后将使用它们的逻辑保存在另一个板条箱中,则可能会容易一些。还有其他一些方法-使用include!技巧,或者使用构建脚本是普通的Rust二进制文件并且还可以包含其他模块的事实-但这会使事情更加复杂。