如何使用serde_json获取JSON文件中的特定项,而无需派生结构体?

14

我有一个复杂的JSON文件,我想从中提取出单个值。我可以定义所有的struct并在它们上面派生Deserialize,但我只想写一点手动代码来获取那个值。老实说,Serde文档让我感到困惑。

我的JSON内容具有以下布局:

{
  "data": [
    {
      "hostname": "a hostname"
    }
  ]
}

我正在寻找进入 data,然后获取数组的第一个元素,并获取 hostname 的值所导航到的价值。

在Haskell中,我会这样做:

newtype Host = Host Text

instance FromJSON Host where
    parseJSON (Object o) = (return . Host) <=< (.: "hostname") <=< (fmap (!! 0) . parseJSON) <=< (.: "data") $ o
    parseJSON _ = mzero

Serde有什么相应的东西吗?


那个链接是给想要实现格式反序列化/解析器的人。这里才是你需要的东西。 - chpio
https://github.com/serde-rs/json#operating-on-untyped-json-values - turbulencetoo
3个回答

17

serde_json 提供了使用 serde_json::Value 的通用 JSON 值类型:

use serde_json::Value;

// input variable
let input: &str = "{...}";

// parse into generic JSON value
let root: Value = serde_json::from_str(input)?;

// access element using .get()
let hostname: Option<&str> = root.get("data")
    .and_then(|value| value.get(0))
    .and_then(|value| value.get("hostname"))
    .and_then(|value| value.as_str());

// hostname is Some(string_value) if .data[0].hostname is a string,
// and None if it was not found
println!("hostname = {:?}", hostname); // = Some("a hostname")

(完整的游乐场示例)


7

如果您可以接受在值缺失或格式错误时返回 Value::Null,我建议使用 Index 语法[...])。如果您想以不同的方式处理缺失/格式错误的情况,请使用 get 方法:

fn main() {
    let json_value = serde_json::json!({
      "data": [
        {
          "hostname": "a hostname"
        }
      ]
    });

    let host = &json_value["data"][0]["hostname"];
    println!("Host: {:?}", host);
}

你也可以通过 pointer 使用 JSON Pointer

let host = json_value.pointer("/data/0/hostname");
println!("Host: {:?}", host);

你推荐使用指针还是索引语法?或者使用.get(...)方法? - Jake Ireland
1
@JakeIreland 这取决于您是否期望该值存在(因此使用可能会出现错误的内容)。 - Shepmaster
1
这应该是答案,但是根据实现和文档,索引不会导致恐慌:https://docs.rs/serde_json/1.0.62/src/serde_json/value/index.rs.html#175-213 - Raz0rwire

2
我会链接 扁平化结构
use serde::{Serialize, Deserialize};
use serde_json::Value;

#[derive(Serialize, Deserialize)]
struct Payload {
    data: Vec<Data>,

    #[serde(flatten)]
    _: HashMap<String, Value>,
}

#[derive(Serialize, Deserialize)]
struct Data {
    hostname: String,

    #[serde(flatten)]
    _: HashMap<String, Value>,
}

let payload: Payload = serde_json::from_str(your_string)?;
assert_eq!(payload.data.0.hostname, "a hostname");

其实你的哈希表并不需要,有趣的是,从性能角度来看,这个答案是最好的。使用json::Value非常慢。但是请删除你无用的哈希表。 - Stargateur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接