Rust 解析動態 JSON

JSON 可能是最常用的數據結構,可以將它用於配置、數據庫等。

json 數據結構的最佳實踐是爲每個屬性提供一個靜態 key,但有時我們需要處理動態 key。

serde_json

在 rust 中,我們使用的是 serde_json 庫,它有很好用的 api。讓我們看看如何用 serde_json 解析 json。

讓我們將 serde 依賴添加到 Cargo.toml 中。我們將使用 serde 將數據序列化爲 struct:

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

現在讓我們解析一些帶有靜態 key 屬性的 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(())
}

使用靜態 key 屬性,我們可以很容易地向結構聲明鍵和類型。但如果 key 是動態的,我們就無法這麼處理。

動態 Json

幸運的是,serde_json 有個枚舉 Value 來處理 json 的數據結構:

enum Value {
    Null,
    Bool(bool),
    Number(Number),
    String(String),
    Array(Vec<Value>),
    Object(Map<String, Value>),
}

有了這個 enum,我們可以檢查每個 key 並對其進行一些序列化。現在,假設我們有這個 json:

{
    "plugins"{
        "width"[["w",["width"]]],
        "height"[["h",["height"]]],
        "z_index"[["z",["z-index"]]]
    }
}

這看起來很簡單,對吧?我們可以像這樣輕鬆地定義 struct:

#[derive(Serialize, Deserialize)]
struct Data {
    plugins: Plugin,
}

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

但是,如果 plugins 內部的 key 是動態的,例如,如果 z_index 變成了 z-index,並且多於三個 key,該怎麼辦呢?

爲了處理這種情況,我們需要把這個數據轉換爲 Map<String, Value>。現在我們可以將 struct 寫成這樣:

#[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_arr = z_index.as_array().unwrap();    
let mut data = Map::new();

for item in z_index_arr {
    if item.get(0).unwrap().is_string() {
        let key = item.get(0).unwrap().as_str().unwrap().to_string();
        let variants = item.get(1).unwrap().clone();
        data.insert(key, variants);
    }
}

現在我們可以這樣訪問 z-index 的值:

let arr = data.get("z").unwrap();
println!("{}", arr);

完整代碼如下:

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

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

fn main() -> Result<(){
    let json = r#"
        {
            "plugins": {
                "width": [["w",["width"]]],
                "height": [["h",["height"]]],
                "z_index": [["z",["z-index"]]]
            }
        }
    "#;

    let data: Data = serde_json::from_str(json)?;
    let z_index = data.plugins.get("z_index").unwrap();
    let z_index_arr = z_index.as_array().unwrap();    
    let mut data = Map::new();

    for item in z_index_arr {
        if item.get(0).unwrap().is_string() {
            let key = item.get(0).unwrap().as_str().unwrap().to_string();
            let variants = item.get(1).unwrap().clone();
            data.insert(key, variants);
        }
    }

    let arr = data.get("z").unwrap();
    println!("{}", arr);

    Ok(())
}

就這些了,可以查看 serde 的文檔瞭解更多使用方法。

本文翻譯自:

https://www.ahmadrosid.com/blog/rust-parsing-dynamic-json

coding 到燈火闌珊 專注於技術分享,包括 Rust、Golang、分佈式架構、雲原生等。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ehUa3Fn7zg_oUwuD03Ufyw