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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
|
extern crate rand;
#[macro_use]
extern crate serde_derive;
extern crate serde_qs as qs;
extern crate serde_urlencoded as urlencoded;
use rand::seq::SliceRandom;
use std::collections::HashMap;
use qs::Config;
#[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);
println!();
// 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:
//
// (For this round trip to work, it's necessary to parse the query string
// in non-strict mode, to allow parsing of url_encoded square brackets
// in the key. See the lib.rs documentation for why).
let qs_non_strict = Config::new(5, false);
let params: QueryParams = qs_non_strict.deserialize_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);
println!();
// 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::rng();
for _ in 0..10 {
let mut acc = String::new();
inputs.shuffle(&mut rng);
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);
// Enums are now fully supported! Most formats should work with varying
// results.
#[derive(Deserialize, Debug, PartialEq, Serialize)]
enum AdjTaggedEnum {
A,
B(bool),
S(String),
V { id: u8, v: String },
}
#[derive(Deserialize, Debug, PartialEq, Serialize)]
struct EnumQuery {
e: AdjTaggedEnum,
}
let example_params = EnumQuery {
e: AdjTaggedEnum::B(false),
};
// encodes as:
// "e[B]=false"
let encoded = qs::to_string(&example_params).unwrap();
println!("`serde_qs` to_string for enum:\n\t{:?}", encoded);
let params: EnumQuery = qs::from_str(&encoded).unwrap();
println!("`serde_qs` from_str for enum:\n\t{:?}", params);
println!();
let example_params = EnumQuery {
e: AdjTaggedEnum::A,
};
// encodes as:
// "e=A"
let encoded = qs::to_string(&example_params).unwrap();
println!("`serde_qs` to_string for enum:\n\t{:?}", encoded);
let params: EnumQuery = qs::from_str(&encoded).unwrap();
println!("`serde_qs` from_str for enum:\n\t{:?}", params);
println!();
}
|