Rust中如何解析动态JSON? - ahmadrosid


JSON 可能是最常用的数据结构。您可以将其用于配置、数据库等。
json 数据结构的最佳实践都是针对每个属性都有静态键的情况,但但有时我们需要处理动态键。
 
serde_json
在 rust 中,我们使用的是 serde_json 库,它有很好的 api 可以使用。那么我们来看看如何用serde_json解析json。
让我们将 serde 依赖添加到Cargo.toml。我们将使用serde将我们的数据序列化为 struct。

serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

现在让我们解析一些带有静态键属性的 json:

use serde::{Deserialize, Serialize};
use serde_json::Result;

#[derive(Serialize, Deserialize)]
struct User {
    username: String,
    age: usize,
    email: String,
    full_name: Vec<String>,
}

pub fn main() -> Result<()> {
    let data = r#"
        {
            "username": "ahmadrosid",
            "email": "alahmadrosid@gmail.com",
            "age": 27,
            "full_name": [
                "Ahmad",
                "Rosid"
            ]
        }"#;

    let u: User = serde_json::from_str(data)?;

    println!("Hi {}", u.full_name[0]);

    Ok(())
}

使用静态键属性,我们可以轻松地向结构声明键和类型。但是,如果密钥是动态的,我们将无法处理。
 
动态Json
幸运的是 serde_json 有这个枚举来处理 json 数据结构:
enum Value {
    Null,
    Bool(bool),
    Number(Number),
    String(String),
    Array(Vec<Value>),
    Object(Map<String, Value>),
}

使用这个枚举,我们可以检查每个键并对其进行一些序列化。现在,假设我们有这个 json:
{
    "plugins": {
        "width": [["w",["width"]]],
        "height": [["h",["height"]]],
        "z_index": [["z",["z-index"]]],
    }
}

看起来很简单吧?我们可以像这样轻松地声明结构。
#[derive(Serialize, Deserialize)]
struct Data {
    plugins: Plugin,
}

#[derive(Serialize, Deserialize)]
struct Plugin {
    width: Vec<Value>,
    height: Vec<Value>,
    z_index: Vec<Value>,
}

但是,如果插件中的键是动态的,例如,如果z_index变成了z-index,而且键的数量超过了例子中的三个,怎么办?

#[derive(Serialize, Deserialize)]
struct Data {
    plugins: Map<String, Value>,
}

现在我们可以像这样访问数据:
let data: Data = serde_json::from_str(json)?;
let z_index: Value = data.get("z-index").unwrap();

要把Value变成数组,我们可以这样做:

let z_index_arr: Vec<&Value> = z_index.as_array().unwrap();


我们也可以通过将z_index里面的值转化为键值来进行改进,它是一个数组。正如你所看到的,z_index的值是数组中的数组。

[
    [
        "z",
        [
            "z-index"
        ]
    ]
]

让我们把这个值变成:
{
    "z": ["z-index"]
}

为了做到这一点,我们将直接使用Map<String, Value>:

let z_index: Vec<&Value> = data.get("z-index").unwrap().as_array().unwrap();
let mut data = Map::new();

for item in z_index {
    if item.get(0)?.is_string() {
        let key = item.get(0)?.as_str()?.to_string();
        let variants = item.get(1)?.clone();
    }
}
小提示! 如果你不想每次转换枚举值时都使用unwrap(),只需使用? 并用Option<()>来包装你的函数

现在我们可以这样访问z-index的值:
let arr: Vec<&Value> = data.get("z")?;

serde 的更多文档