我有这个JSON:
{
"argument0": {
"argument1": "test",
"argument2": {
"argument3": "test3"
}
}
}
我需要使用某种类型的递归结构,例如Rust中的HashMap<String, _>
。密钥应始终为String
,但值可以是String
或相同的Argument
结构。
#[derive(Clone, RustcDecodable, RustcEncodable)]
struct Argument {
key: String
value: String Or Argument
}
我怎样才能做到这一点?
答案 0 :(得分:2)
您在这里有一些不同的问题。
首先,您希望能够定义一个数据类型,该数据类型可以是一种类型,也可以是另一种类型,但不能同时使用。这就是Rust的enum
data type的用途。
enum Value {
String(String),
Argument(Argument),
}
此Value
类型可以包含String
或Argument
,但不能同时包含两者。
现在,我们需要定义Argument
类型。在您的示例中,参数可以包含任意字段名称,因此我们不能仅定义struct
。相反,我们可以使用标准库中的map集合将String
映射到Value
,例如BTreeMap
。我们还将定义一个type alias,以便我们可以在程序的其他位置使用名称Argument
代替BTreeMap<String, Argument>
。
use std::collections::BTreeMap;
type Argument = BTreeMap<String, Argument>;
现在我们已经成功定义了类型,让我们使用serde库来定义其序列化行为。 Serde可以从Rust标准库中自动序列化类型,并且用户结构可以实现或派生Serialize
和Deserialize
特征以将功能添加到自己的类型中。
对于大多数结构,我们只需添加#[derive(Serialize)]
和/或#[derive(Deserialize)]
即可实现序列化所需的特征。在这种情况下,我们希望将enum
的反序列化自定义为untagged,因此它只发出枚举的值,而不是以“ String”或“ Argument”为键的对象。相反,我们只希望JSON包含值。为此,我们向结构#[serde(untagged)]
添加了一个特殊属性。
这是一个简短的Rust程序,演示了上述概念。该程序将读取您的JSON示例,并打印代表数据的Rust类型的Debug
表示形式。
#[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);
}