1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
extern crate csv;
extern crate rand;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_qs as qs;
extern crate serde_urlencoded as urlencoded;
use rand::Rng;
use std::collections::HashMap;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct Address {
city: String,
postcode: String,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct QueryParams {
id: u8,
name: String,
address: Address,
phone: u32,
user_ids: Vec<u8>,
}
fn main() {
// Encodes as:
// "user_ids%5B3%5D=4&user_ids%5B2%5D=3&address%5Bcity%5D=Carrot+City&\
// id=42&address%5Bpostcode%5D=12345&name=Acme&user_ids%5B0%5D=1&\
// phone=12345&user_ids%5B1%5D=2"
let example_params = QueryParams {
id: 42,
name: "Acme".to_string(),
phone: 12345,
address: Address {
city: "Carrot City".to_string(),
postcode: "12345".to_string(),
},
user_ids: vec![1, 2, 3, 4],
};
// Naive approach: manually parameters in a map. Painful.
let mut map = HashMap::<&str, &str>::new();
map.insert("id", "42");
map.insert("name", "Acme");
map.insert("phone", "12345");
map.insert("address[city]", "Carrot City");
map.insert("address[postcode]", "12345");
map.insert("user_ids[0]", "1");
map.insert("user_ids[1]", "2");
map.insert("user_ids[2]", "3");
map.insert("user_ids[3]", "4");
// Note this will be in some random order due to ordering of keys in map.
let encoded = qs::to_string(&map).unwrap();
println!("`serde_qs` to_string for map:\n\t{}", encoded);
// In this form, can also simply use `serde_urlencoded`:
let encoded = urlencoded::to_string(&map).unwrap();
println!("`serde_urlencoded` to_string for map:\n\t{}", encoded);
// Given this encoded string, you can recover the original map
// as a list of pairs using serde_urlencoded:
let pairs: Vec<(String, String)> = urlencoded::from_str(&encoded).unwrap();
println!("`serde_urlencoded` from_str to pairs:\n\t{:?}", pairs);
// However, the best way is to use serde_qs to deserialize the entire thing
// into a struct:
let params: QueryParams = qs::from_str(&encoded).unwrap();
assert_eq!(params, example_params);
println!("`serde_qs` from_str to struct:\n\t{:?}", params);
// Similarly, we can serialize this structure using `serde_qs`:
let encoded = qs::to_string(¶ms).unwrap();
println!("`serde_qs` to_string for struct:\n\t{:?}", encoded);
// One nice feature is that this gives deterministic encodings:
let encoded2 = qs::to_string(¶ms).unwrap();
assert_eq!(encoded, encoded2);
// An advantage of `serde_qs` for deserializing, is that it is robust
// against different orderings of inputs:
let mut inputs = vec!["id=42",
"name=Acme",
"phone=12345",
"address[city]=Carrot+City",
"address[postcode]=12345",
"user_ids[0]=1",
"user_ids[1]=2",
"user_ids[2]=3",
"user_ids[3]=4"];
let mut rng = rand::thread_rng();
for _ in 0..10 {
let mut acc = String::new();
rng.shuffle(&mut inputs);
for input in &inputs {
acc += input;
acc += "&";
}
// remove trailing character
acc.pop();
let params: QueryParams = qs::from_str(&acc).unwrap();
assert_eq!(params, example_params);
}
// By default, `serde_qs` uses arrays with indices to denote position.
// However, if omitted, will use input order:
let encoded = "id=42&name=Acme&phone=12345&address[city]=Carrot+City&\
address[postcode]=12345&\
user_ids[]=1&\
user_ids[]=2&\
user_ids[]=3&\
user_ids[]=4";
let params: QueryParams = qs::from_str(encoded).unwrap();
assert_eq!(params, example_params);
// Indices do not necessarily need to be continuous:
let encoded = "id=42&name=Acme&phone=12345&address[city]=Carrot+City&\
address[postcode]=12345&\
user_ids[1]=2&\
user_ids[0]=1&\
user_ids[12]=3&\
user_ids[512]=4";
let params: QueryParams = qs::from_str(encoded).unwrap();
assert_eq!(params, example_params);
}
|