I am trying to parse json response from an API into a struct using serde_json
.
Here's a very simplified sample response
{
"items": [
{
"label": {
"volume": {
"a": "...",
"b": "..."
}
}
},
{
"label": {
"volume": {
"a": "...",
"b": "..."
}
}
},
...
...
]
}
Response is rather dense but I am interested in very few values inside "volume" object.
struct Resp {
a: AType,
b: BType,
}
Parsing would be simple enough if I were to use derive attributes but unfortunately the values associated with "volume"
are very chaotic and thus require manual deserialize
implementation. (Although that was not as difficult as I'd imagined thanks to serde
awesome docs!)
So basically what I am looking for is a way to point that manually written deserializer to the inner list structure and collect the parsed values into Vec<Resp>
.
So far I've found two very hacky ways of going about that:
- Using
serde_json::from_value
which lead to some very hard to read code
serde_json::from_value::<Vec<Resp>>(
reqwest::get(req)
.await
.map_err(...)?
.json::<serde_json::Value>()
.await
.map_err(...)?["items"]
.take(),
) // "items" is an array of maps.
.map_err(...)?
.iter_mut()
.map(|v: &mut serde_json::Value| {
serde_json::from_value(v["label"].take()).map_err(...)
}) // Each map contains "label" key with a map value.
...
...
- Using intermediate structs with
Deserialize
attributes
#[derive(Deserialize)]
struct Items {
items: Vec<Label>,
}
#[derive(Deserialize)]
struct Label{
label: Volume,
}
#[derive(Deserialize)]
struct Label{
volume: serde_json::Value,
}
serde_json::from_value::<Vec<Resp>>(
reqwest::get(req)
.await
.map_err(...)?
.json::<Items>()
.await
...
...
which I think is essentially the same thing just more readable.
So I was wondering if there was a better way of targeting an inner structure with serde
's deserialize
fn.
P.S. I also tried volume: Resp
in Label
struct but that doesn't work, throws error like this reqwest::Error { kind: Decode, source: Error("expected ',' or '}' ...
)