Reputation: 3
Could you please help me with the following problem?
I have a JSON:
{
"some_other_field": "value",
"option1": "value1",
"option2": "value2",
[...]
"option10": "value10",
}
There are up to X optionX
fields which are all optional
I would like to deserialize it to a struct with a single Vec<String>
where the index in the vector corresponds to the suffix in the field name:
options[1] -> value of `option1`
options[10] -> value of `option10`
Here's my struct:
struct Data {
some_other_field: String,
options: Vec<String>,
}
I tried many things including writing a custom deserializer but I cannot get pass this problem :(
Any help will be greatly appreciated.
Upvotes: 0
Views: 1876
Reputation: 528
You can construct an array of strings by iterating over the parsed value, then this to your parent struct.
use serde_json::{Result, Value};
fn main() -> Result<()> {
let data = r#"
{
"some_other_field": "value",
"option1": "value1",
"option2": "value2",
"option8": "value8",
"option10": "value10"
}"#;
let v: Value = serde_json::from_str(data)?;
let mut k = v
.as_object()
.unwrap()
.iter()
.filter(|(k, val)| k.contains("option") && val.as_str() != None)
.map(|(k, vs)| {
(
k.replace("option", "").parse::<usize>().unwrap(),
vs.as_str().unwrap().to_string(),
)
})
.collect::<Vec<(usize, String)>>();
k.sort();
let mut result: Vec<String> = vec![];
let mut it: usize = 0;
let mut idx: usize = 0;
let mut tidx: usize = 0;
while idx < k.len() {
tidx = k[idx].0;
while it != tidx {
result.push("".to_string());
it += 1;
}
result.push(k[idx].to_owned().1);
idx += 1;
it += 1;
}
println!("{:?}", result);
Ok(())
}
Output:
["", "value1", "value2", "", "", "", "", "", "value8", "", "value10"]
Upvotes: 0
Reputation: 6584
One way to solve this is to serialize the JSON data using serde_json
, then use a temporary map to pick these values. I skipped a lot of error checks, but here's the outline:
use serde_json::{Result, Value};
use std::collections::HashMap;
#[derive(Debug)]
struct Data {
some_other_field: String,
options: Vec<String>,
}
fn text_to_data(text: &str) -> Result<Data> {
let prefix: &'static str = "option";
// Deserialize JSON
let value: Value = serde_json::from_str(text)?;
let value_map = value.as_object().unwrap();
// Filter keys for "option*", cut prefix, convert to integer and store it in a map
let options_map: HashMap<usize, &str> = value_map.iter()
.filter(|it| it.0.starts_with(prefix))
.map(|it| (it.0[prefix.len()..].parse::<usize>().unwrap(), it.1.as_str().unwrap()))
.collect();
// Get the maximum of options
let options_count = options_map.iter().map(|it| it.0).max().unwrap();
// Collect values to a vector or use empty string as default
let options: Vec<String> = (0..=*options_count)
.map(|it| options_map.get(&it).unwrap_or(&"").to_string()).collect();
// Also access other fields in JSON
let some_other_field =
value.get("some_other_field").unwrap().as_str().unwrap().to_string();
Ok(Data { some_other_field, options })
}
fn main() {
let data = r#"
{
"some_other_field": "value",
"option1": "value1",
"option2": "value2",
"option10": "value10"
}"#;
let x = text_to_data(data);
println!("{:?}", x);
}
Output:
Ok(Data { some_other_field: "value", options: ["", "value1", "value2", "", "", "", "", "", "", "", "value10"] })
Upvotes: 1