extern crate csv; extern crate serde; #[macro_use] extern crate serde_derive; extern crate serde_qs as qs; use serde::de::DeserializeOwned; use std::default::Default; #[derive(Debug, PartialEq, Deserialize, Serialize)] struct Query { #[serde(deserialize_with = "from_csv")] r: Vec, s: u8, } fn main() { let q = "s=12&r=1,2,3"; let q: Query = qs::from_str(q).unwrap(); println!("{:?}", q); } #[test] fn deserialize_sequence() { let q = "s=12&r=1,2,3"; let q: Query = qs::from_str(q).unwrap(); let expected = Query { r: vec![1, 2, 3], s: 12, }; assert_eq!(q, expected); } fn from_csv<'de, D, T>(deserializer: D) -> Result, D::Error> where D: serde::Deserializer<'de>, T: DeserializeOwned + std::str::FromStr, ::Err: std::fmt::Debug, { deserializer.deserialize_str(CSVVecVisitor::::default()) } /// Visits a string value of the form "v1,v2,v3" into a vector of bytes Vec struct CSVVecVisitor(std::marker::PhantomData); impl Default for CSVVecVisitor { fn default() -> Self { CSVVecVisitor(std::marker::PhantomData) } } impl<'de, T: DeserializeOwned + std::str::FromStr> serde::de::Visitor<'de> for CSVVecVisitor where ::Err: std::fmt::Debug, // handle the parse error in a generic way { type Value = Vec; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { write!(formatter, "a str") } fn visit_str(self, s: &str) -> std::result::Result where E: serde::de::Error, { // Treat the comma-separated string as a single record in a CSV. let mut rdr = csv::ReaderBuilder::new() .has_headers(false) .from_reader(s.as_bytes()); // Try to get the record and collect its values into a vector. let mut output = Vec::new(); for result in rdr.records() { match result { Ok(record) => { for field in record.iter() { output.push( field .parse::() .map_err(|_| E::custom("Failed to parse field"))?, ); } } Err(e) => { return Err(E::custom(format!( "could not deserialize sequence value: {:?}", e ))); } } } Ok(output) } }