use crate::error::{Result, ScraperError};
use jsonpath_lib as jsonpath;
use serde::de::DeserializeOwned;
#[derive(Debug)]
pub struct Json {
value: serde_json::Value,
}
impl Json {
pub fn new(json: &str) -> Result<Self> {
let value = serde_json::from_str(json)?;
Ok(Self { value })
}
pub fn select<T: DeserializeOwned>(&self, path: &str) -> Result<Vec<T>> {
jsonpath::Selector::new()
.str_path(path)?
.value(&self.value)
.select_as()
.map_err(ScraperError::from)
}
pub fn select_one<T: DeserializeOwned>(&self, path: &str) -> Result<T> {
let result = jsonpath::Selector::new()
.str_path(path)?
.value(&self.value)
.select()?;
let v = result
.first()
.ok_or_else(|| {
ScraperError::JsonPathMatchError(format!(
"The \"{}\" jsonpath did not find data in json",
path
))
})?
.to_owned();
Ok(serde_json::from_value::<T>(v.to_owned())?)
}
pub fn select_as_str(&self, path: &str) -> Result<String> {
jsonpath::Selector::new()
.str_path(path)?
.value(&self.value)
.select_as_str()
.map_err(ScraperError::from)
}
pub fn select_one_as_str(&self, path: &str) -> Result<String> {
let result = jsonpath::Selector::new()
.str_path(path)?
.value(&self.value)
.select()?;
let v = result
.first()
.ok_or_else(|| {
ScraperError::JsonPathMatchError(format!(
"The \"{}\" jsonpath did not find data in json",
path
))
})?
.to_owned();
Ok(v.to_string())
}
}
#[cfg(test)]
mod tests {
#[test]
#[allow(clippy::get_first)]
fn test_jsonpath() {
use super::Json;
use serde::Deserialize;
let json = r#"
{
"time":"2020.10.12 21:22:34",
"data":[
{"a":"A1","B":"b1","c":1},
{"a":"A2","B":"b2","c":2}
]
}
"#;
let json = Json::new(json).unwrap();
assert_eq!(
json.select_one_as_str("$.time").unwrap(),
r#""2020.10.12 21:22:34""#
);
#[derive(Deserialize)]
struct DataItem {
a: String,
#[serde(rename = "B")]
b: String,
c: i32,
}
let data: Vec<DataItem> = json.select("$.data[*]").unwrap();
assert_eq!(data.len(), 2);
let d1 = data.get(0).unwrap();
assert_eq!(d1.a, "A1");
assert_eq!(d1.b, "b1");
assert_eq!(d1.c, 1);
let d2 = data.get(1).unwrap();
assert_eq!(d2.a, "A2");
assert_eq!(d2.b, "b2");
assert_eq!(d2.c, 2);
}
}