在递归结构中序列化 JSON [英] Serialize JSON in a recursive struct
问题描述
我有这个 JSON:
{
"argument0": {
"argument1": "test",
"argument2": {
"argument3": "test3"
}
}
}
我需要将某种递归结构与 Rust 中的 HashMap
之类的方法一起使用.键应始终是 String
,但值可以是 String
或相同的 Argument
结构.
I need to use some kind of recursive struct with methods like the HashMap<String, _>
in Rust. The key should always be a String
but the value can be a String
or the same Argument
struct.
#[derive(Clone, RustcDecodable, RustcEncodable)]
struct Argument {
key: String
value: String Or Argument
}
我怎样才能做到这一点?
How can I achieve this?
推荐答案
这里有几个明显的问题.
You have a few distinct problems here.
首先,您希望能够定义一种数据类型,该数据类型可以是一种类型或另一种类型,但不能同时是两种类型.这就是 Rust 的 enum
数据类型 适用于.
First, you want to be able to define a data type that can be either one type or another type, but not both. This is what Rust's enum
data type is intended for.
enum Value {
String(String),
Argument(Argument),
}
此 Value
类型可以包含 String
或 Argument
,但不能同时包含两者.
This Value
type can contain either a String
or an Argument
, but not both.
现在,我们需要定义 Argument
类型.在您的示例中,参数可以包含任意字段名称,因此我们不能只定义 struct
.相反,我们可以使用标准库中的映射集合将 String
s 映射到 Value
s,例如 BTreeMap
.我们还将定义一个 输入别名 以便我们可以在程序的其他地方使用名称Argument
而不是BTreeMap
.
Now, we need to define the Argument
type. In your example, an argument can contain arbitrary field names, so we can't just define a struct
. Instead, we can use a map collection from the standard library to map String
s to Value
s, such as BTreeMap
. We'll also define a type alias so that we can use the name Argument
instead of BTreeMap<String, Argument>
elsewhere in the program.
use std::collections::BTreeMap;
type Argument = BTreeMap<String, Argument>;
既然我们已经成功定义了类型,让我们使用 serde 库定义它的序列化行为.Serde 可以自动序列化来自 Rust 标准库的类型,并且用户结构可以实现或派生 Serialize
和 Deserialize
特征以将功能添加到他们自己的类型中.
Now that we've successfully defined the type, let's define its serialization behavior using the serde library. Serde can automatically serialize types from the Rust standard library, and user structs can implement or derive the Serialize
and Deserialize
traits to add the functionality to their own types.
对于大多数结构体,我们只需添加一个 #[derive(Serialize)]
和/或 #[derive(Deserialize)]
来实现序列化所需的特征.在这种情况下,我们希望将 enum
的反序列化自定义为 未标记,所以它只发出枚举的值,而不是以String"或Argument"作为键的对象.相反,我们只希望 JSON 包含该值.我们通过向结构体添加一个特殊属性来实现这一点,#[serde(untagged)]
.
For most structs, we can just add a #[derive(Serialize)]
and/or #[derive(Deserialize)]
to implement the necessary traits for serialization. In this case, we want to customize the deserialization of our enum
to be untagged, so it just emits the value of the enum, not an object with "String" or "Argument" as the key. Instead, we just want the JSON to contain the value. We do this by adding a special attribute to the struct, #[serde(untagged)]
.
这是一个简短的 Rust 程序,用于演示上述概念.该程序将读取您的 JSON 示例,并打印 Debug
表示数据的 Rust 类型的表示.
Here's a short Rust program that demonstrates the above concepts. This program will read your JSON example, and print the Debug
representation of a Rust type that represents the data.
#[macro_use]
extern crate serde_derive; // 1.0.78
extern crate serde; // 1.0.78
extern crate serde_json; // 1.0.27
use std::collections::BTreeMap;
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
enum Value {
String(String),
Argument(Argument),
}
type Argument = BTreeMap<String, Value>;
fn main() {
let argument: Argument = serde_json::from_str(
r#"{
"argument0": {
"argument1": "test",
"argument2": {
"argument3": "test3"
}
}
}"#,
).unwrap();
println!("{:?}", argument);
}
这篇关于在递归结构中序列化 JSON的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!