I have a complex JSON file and I'd like to extract only a single value out of it. I could define all of the struct
s and derive Deserialize
on all of them, but I'd like to just write a little manual code to pull that one value out. The Serde documentation, quite frankly, just confused me.
My JSON content has the following layout:
{
"data": [
{
"hostname": "a hostname"
}
]
}
I'm looking for the value navigated to by going into data
, then taking the first element of the array, and taking the value of hostname
.
In Haskell, I'd do it like this:
newtype Host = Host Text
instance FromJSON Host where
parseJSON (Object o) = (return . Host) <=< (.: "hostname") <=< (fmap (!! 0) . parseJSON) <=< (.: "data") $ o
parseJSON _ = mzero
What's the equivalent for Serde?
serde_json
provides types for generic JSON values with serde_json::Value
:
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")
(full playground example)
If you want to panic if the value is missing or malformed, I'd use the Index
syntax ([...]
). If you want to handle the missing / malformed case, use the get
method:
fn main() {
let json_value = serde_json::json!({
"data": [
{
"hostname": "a hostname"
}
]
});
let host = &json_value["data"][0]["hostname"];
println!("Host: {:?}", host);
}
You can also use a JSON Pointer via pointer
:
let host = json_value.pointer("/data/0/hostname");
println!("Host: {:?}", host);
I would chain flattened structures
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");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With