reqwest_scraper/
jsonpath.rs

1//!  Use JsonPath to select fields in json response
2//!
3use crate::error::{Result, ScraperError};
4use jsonpath_lib as jsonpath;
5use serde::de::DeserializeOwned;
6
7/// Json Response
8#[derive(Debug)]
9pub struct Json {
10    value: serde_json::Value,
11}
12
13impl Json {
14    /// constructor
15    pub fn new(json: &str) -> Result<Self> {
16        let value = serde_json::from_str(json)?;
17        Ok(Self { value })
18    }
19
20    /// Use jsonpath to select json fragments and convert them into structures
21    pub fn select<T: DeserializeOwned>(&self, path: &str) -> Result<Vec<T>> {
22        jsonpath::Selector::new()
23            .str_path(path)?
24            .value(&self.value)
25            .select_as()
26            .map_err(ScraperError::from)
27    }
28
29    /// Use jsonpath to select json string fields
30    pub fn select_one<T: DeserializeOwned>(&self, path: &str) -> Result<T> {
31        let result = jsonpath::Selector::new()
32            .str_path(path)?
33            .value(&self.value)
34            .select()?;
35        let v = result
36            .first()
37            .ok_or_else(|| {
38                ScraperError::JsonPathMatchError(format!(
39                    "The \"{}\" jsonpath did not find data in json",
40                    path
41                ))
42            })?
43            .to_owned();
44        Ok(serde_json::from_value::<T>(v.to_owned())?)
45    }
46
47    /// Use jsonpath to select json fields as string
48    pub fn select_as_str(&self, path: &str) -> Result<String> {
49        jsonpath::Selector::new()
50            .str_path(path)?
51            .value(&self.value)
52            .select_as_str()
53            .map_err(ScraperError::from)
54    }
55
56    /// Use jsonpath to select one json fields as string
57    pub fn select_one_as_str(&self, path: &str) -> Result<String> {
58        let result = jsonpath::Selector::new()
59            .str_path(path)?
60            .value(&self.value)
61            .select()?;
62        let v = result
63            .first()
64            .ok_or_else(|| {
65                ScraperError::JsonPathMatchError(format!(
66                    "The \"{}\" jsonpath did not find data in json",
67                    path
68                ))
69            })?
70            .to_owned();
71        Ok(v.to_string())
72    }
73}
74
75#[cfg(test)]
76mod tests {
77
78    #[test]
79    #[allow(clippy::get_first)]
80    fn test_jsonpath() {
81        use super::Json;
82        use serde::Deserialize;
83        let json = r#"
84        {
85            "time":"2020.10.12 21:22:34",
86            "data":[
87                {"a":"A1","B":"b1","c":1},
88                {"a":"A2","B":"b2","c":2}
89            ]
90        }
91        "#;
92
93        let json = Json::new(json).unwrap();
94
95        assert_eq!(
96            json.select_one_as_str("$.time").unwrap(),
97            r#""2020.10.12 21:22:34""#
98        );
99
100        #[derive(Deserialize)]
101        struct DataItem {
102            a: String,
103            #[serde(rename = "B")]
104            b: String,
105            c: i32,
106        }
107
108        let data: Vec<DataItem> = json.select("$.data[*]").unwrap();
109        assert_eq!(data.len(), 2);
110
111        let d1 = data.get(0).unwrap();
112        assert_eq!(d1.a, "A1");
113        assert_eq!(d1.b, "b1");
114        assert_eq!(d1.c, 1);
115
116        let d2 = data.get(1).unwrap();
117        assert_eq!(d2.a, "A2");
118        assert_eq!(d2.b, "b2");
119        assert_eq!(d2.c, 2);
120    }
121}