在递归结构中序列化 JSON [英] Serialize JSON in a recursive struct

查看:81
本文介绍了在递归结构中序列化 JSON的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个 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 类型可以包含 StringArgument,但不能同时包含两者.

This Value type can contain either a String or an Argument, but not both.

现在,我们需要定义 Argument 类型.在您的示例中,参数可以包含任意字段名称,因此我们不能只定义 struct.相反,我们可以使用标准库中的映射集合将 Strings 映射到 Values,例如 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 Strings to Values, 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 标准库的类型,并且用户结构可以实现或派生 SerializeDeserialize 特征以将功能添加到他们自己的类型中.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆