Skip to content

Commit b5a6ccc

Browse files
authored
Merge pull request #79 from stefansundin/skip_serializing_if
Add `#[serde(skip_serializing_if = "Option::is_none")]` to all fields
2 parents 39cce16 + 65def4b commit b5a6ccc

File tree

3 files changed

+126
-0
lines changed

3 files changed

+126
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ env_logger = "0.11"
3434
criterion = "0.5"
3535
fake = "2.4"
3636
rayon = "1.5"
37+
serde_json = "1.0"
3738

3839
[[bench]]
3940
name = "lookup"

src/maxminddb/geoip2.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,88 +4,129 @@ use serde::{Deserialize, Serialize};
44
#[derive(Deserialize, Serialize, Clone, Debug)]
55
pub struct Country<'a> {
66
#[serde(borrow)]
7+
#[serde(skip_serializing_if = "Option::is_none")]
78
pub continent: Option<country::Continent<'a>>,
9+
#[serde(skip_serializing_if = "Option::is_none")]
810
pub country: Option<country::Country<'a>>,
11+
#[serde(skip_serializing_if = "Option::is_none")]
912
pub registered_country: Option<country::Country<'a>>,
13+
#[serde(skip_serializing_if = "Option::is_none")]
1014
pub represented_country: Option<country::RepresentedCountry<'a>>,
15+
#[serde(skip_serializing_if = "Option::is_none")]
1116
pub traits: Option<country::Traits>,
1217
}
1318

1419
/// GeoIP2 City record
1520
#[derive(Deserialize, Serialize, Clone, Debug)]
1621
pub struct City<'a> {
1722
#[serde(borrow)]
23+
#[serde(skip_serializing_if = "Option::is_none")]
1824
pub city: Option<city::City<'a>>,
25+
#[serde(skip_serializing_if = "Option::is_none")]
1926
pub continent: Option<city::Continent<'a>>,
27+
#[serde(skip_serializing_if = "Option::is_none")]
2028
pub country: Option<city::Country<'a>>,
29+
#[serde(skip_serializing_if = "Option::is_none")]
2130
pub location: Option<city::Location<'a>>,
31+
#[serde(skip_serializing_if = "Option::is_none")]
2232
pub postal: Option<city::Postal<'a>>,
33+
#[serde(skip_serializing_if = "Option::is_none")]
2334
pub registered_country: Option<city::Country<'a>>,
35+
#[serde(skip_serializing_if = "Option::is_none")]
2436
pub represented_country: Option<city::RepresentedCountry<'a>>,
37+
#[serde(skip_serializing_if = "Option::is_none")]
2538
pub subdivisions: Option<Vec<city::Subdivision<'a>>>,
39+
#[serde(skip_serializing_if = "Option::is_none")]
2640
pub traits: Option<city::Traits>,
2741
}
2842

2943
/// GeoIP2 Enterprise record
3044
#[derive(Deserialize, Serialize, Clone, Debug)]
3145
pub struct Enterprise<'a> {
3246
#[serde(borrow)]
47+
#[serde(skip_serializing_if = "Option::is_none")]
3348
pub city: Option<enterprise::City<'a>>,
49+
#[serde(skip_serializing_if = "Option::is_none")]
3450
pub continent: Option<enterprise::Continent<'a>>,
51+
#[serde(skip_serializing_if = "Option::is_none")]
3552
pub country: Option<enterprise::Country<'a>>,
53+
#[serde(skip_serializing_if = "Option::is_none")]
3654
pub location: Option<enterprise::Location<'a>>,
55+
#[serde(skip_serializing_if = "Option::is_none")]
3756
pub postal: Option<enterprise::Postal<'a>>,
57+
#[serde(skip_serializing_if = "Option::is_none")]
3858
pub registered_country: Option<enterprise::Country<'a>>,
59+
#[serde(skip_serializing_if = "Option::is_none")]
3960
pub represented_country: Option<enterprise::RepresentedCountry<'a>>,
61+
#[serde(skip_serializing_if = "Option::is_none")]
4062
pub subdivisions: Option<Vec<enterprise::Subdivision<'a>>>,
63+
#[serde(skip_serializing_if = "Option::is_none")]
4164
pub traits: Option<enterprise::Traits<'a>>,
4265
}
4366

4467
/// GeoIP2 ISP record
4568
#[derive(Deserialize, Serialize, Clone, Debug)]
4669
pub struct Isp<'a> {
70+
#[serde(skip_serializing_if = "Option::is_none")]
4771
pub autonomous_system_number: Option<u32>,
72+
#[serde(skip_serializing_if = "Option::is_none")]
4873
pub autonomous_system_organization: Option<&'a str>,
74+
#[serde(skip_serializing_if = "Option::is_none")]
4975
pub isp: Option<&'a str>,
76+
#[serde(skip_serializing_if = "Option::is_none")]
5077
pub mobile_country_code: Option<&'a str>,
78+
#[serde(skip_serializing_if = "Option::is_none")]
5179
pub mobile_network_code: Option<&'a str>,
80+
#[serde(skip_serializing_if = "Option::is_none")]
5281
pub organization: Option<&'a str>,
5382
}
5483

5584
/// GeoIP2 Connection-Type record
5685
#[derive(Deserialize, Serialize, Clone, Debug)]
5786
pub struct ConnectionType<'a> {
87+
#[serde(skip_serializing_if = "Option::is_none")]
5888
pub connection_type: Option<&'a str>,
5989
}
6090

6191
/// GeoIP2 Anonymous Ip record
6292
#[derive(Deserialize, Serialize, Clone, Debug)]
6393
pub struct AnonymousIp {
94+
#[serde(skip_serializing_if = "Option::is_none")]
6495
pub is_anonymous: Option<bool>,
96+
#[serde(skip_serializing_if = "Option::is_none")]
6597
pub is_anonymous_vpn: Option<bool>,
98+
#[serde(skip_serializing_if = "Option::is_none")]
6699
pub is_hosting_provider: Option<bool>,
100+
#[serde(skip_serializing_if = "Option::is_none")]
67101
pub is_public_proxy: Option<bool>,
102+
#[serde(skip_serializing_if = "Option::is_none")]
68103
pub is_residential_proxy: Option<bool>,
104+
#[serde(skip_serializing_if = "Option::is_none")]
69105
pub is_tor_exit_node: Option<bool>,
70106
}
71107

72108
/// GeoIP2 DensityIncome record
73109
#[derive(Deserialize, Serialize, Clone, Debug)]
74110
pub struct DensityIncome {
111+
#[serde(skip_serializing_if = "Option::is_none")]
75112
pub average_income: Option<u32>,
113+
#[serde(skip_serializing_if = "Option::is_none")]
76114
pub population_density: Option<u32>,
77115
}
78116

79117
/// GeoIP2 Domain record
80118
#[derive(Deserialize, Serialize, Clone, Debug)]
81119
pub struct Domain<'a> {
120+
#[serde(skip_serializing_if = "Option::is_none")]
82121
pub domain: Option<&'a str>,
83122
}
84123

85124
/// GeoIP2 Asn record
86125
#[derive(Deserialize, Serialize, Clone, Debug)]
87126
pub struct Asn<'a> {
127+
#[serde(skip_serializing_if = "Option::is_none")]
88128
pub autonomous_system_number: Option<u32>,
129+
#[serde(skip_serializing_if = "Option::is_none")]
89130
pub autonomous_system_organization: Option<&'a str>,
90131
}
91132

@@ -96,33 +137,48 @@ pub mod country {
96137

97138
#[derive(Deserialize, Serialize, Clone, Debug)]
98139
pub struct Continent<'a> {
140+
#[serde(skip_serializing_if = "Option::is_none")]
99141
pub code: Option<&'a str>,
142+
#[serde(skip_serializing_if = "Option::is_none")]
100143
pub geoname_id: Option<u32>,
144+
#[serde(skip_serializing_if = "Option::is_none")]
101145
pub names: Option<BTreeMap<&'a str, &'a str>>,
102146
}
103147

104148
#[derive(Deserialize, Serialize, Clone, Debug)]
105149
pub struct Country<'a> {
150+
#[serde(skip_serializing_if = "Option::is_none")]
106151
pub geoname_id: Option<u32>,
152+
#[serde(skip_serializing_if = "Option::is_none")]
107153
pub is_in_european_union: Option<bool>,
154+
#[serde(skip_serializing_if = "Option::is_none")]
108155
pub iso_code: Option<&'a str>,
156+
#[serde(skip_serializing_if = "Option::is_none")]
109157
pub names: Option<BTreeMap<&'a str, &'a str>>,
110158
}
111159

112160
#[derive(Deserialize, Serialize, Clone, Debug)]
113161
pub struct RepresentedCountry<'a> {
162+
#[serde(skip_serializing_if = "Option::is_none")]
114163
pub geoname_id: Option<u32>,
164+
#[serde(skip_serializing_if = "Option::is_none")]
115165
pub is_in_european_union: Option<bool>,
166+
#[serde(skip_serializing_if = "Option::is_none")]
116167
pub iso_code: Option<&'a str>,
168+
#[serde(skip_serializing_if = "Option::is_none")]
117169
pub names: Option<BTreeMap<&'a str, &'a str>>,
118170
#[serde(rename = "type")]
171+
#[serde(skip_serializing_if = "Option::is_none")]
119172
pub representation_type: Option<&'a str>,
120173
}
121174

122175
#[derive(Deserialize, Serialize, Clone, Debug)]
123176
pub struct Traits {
177+
#[serde(skip_serializing_if = "Option::is_none")]
124178
pub is_anonymous_proxy: Option<bool>,
179+
#[serde(skip_serializing_if = "Option::is_none")]
125180
pub is_anycast: Option<bool>,
181+
#[serde(skip_serializing_if = "Option::is_none")]
126182
pub is_satellite_provider: Option<bool>,
127183
}
128184
}
@@ -136,29 +192,40 @@ pub mod city {
136192

137193
#[derive(Deserialize, Serialize, Clone, Debug)]
138194
pub struct City<'a> {
195+
#[serde(skip_serializing_if = "Option::is_none")]
139196
pub geoname_id: Option<u32>,
140197
#[serde(borrow)]
198+
#[serde(skip_serializing_if = "Option::is_none")]
141199
pub names: Option<BTreeMap<&'a str, &'a str>>,
142200
}
143201

144202
#[derive(Deserialize, Serialize, Clone, Debug)]
145203
pub struct Location<'a> {
204+
#[serde(skip_serializing_if = "Option::is_none")]
146205
pub accuracy_radius: Option<u16>,
206+
#[serde(skip_serializing_if = "Option::is_none")]
147207
pub latitude: Option<f64>,
208+
#[serde(skip_serializing_if = "Option::is_none")]
148209
pub longitude: Option<f64>,
210+
#[serde(skip_serializing_if = "Option::is_none")]
149211
pub metro_code: Option<u16>,
212+
#[serde(skip_serializing_if = "Option::is_none")]
150213
pub time_zone: Option<&'a str>,
151214
}
152215

153216
#[derive(Deserialize, Serialize, Clone, Debug)]
154217
pub struct Postal<'a> {
218+
#[serde(skip_serializing_if = "Option::is_none")]
155219
pub code: Option<&'a str>,
156220
}
157221

158222
#[derive(Deserialize, Serialize, Clone, Debug)]
159223
pub struct Subdivision<'a> {
224+
#[serde(skip_serializing_if = "Option::is_none")]
160225
pub geoname_id: Option<u32>,
226+
#[serde(skip_serializing_if = "Option::is_none")]
161227
pub iso_code: Option<&'a str>,
228+
#[serde(skip_serializing_if = "Option::is_none")]
162229
pub names: Option<BTreeMap<&'a str, &'a str>>,
163230
}
164231
}
@@ -172,63 +239,100 @@ pub mod enterprise {
172239

173240
#[derive(Deserialize, Serialize, Clone, Debug)]
174241
pub struct City<'a> {
242+
#[serde(skip_serializing_if = "Option::is_none")]
175243
pub confidence: Option<u8>,
244+
#[serde(skip_serializing_if = "Option::is_none")]
176245
pub geoname_id: Option<u32>,
177246
#[serde(borrow)]
247+
#[serde(skip_serializing_if = "Option::is_none")]
178248
pub names: Option<BTreeMap<&'a str, &'a str>>,
179249
}
180250

181251
#[derive(Deserialize, Serialize, Clone, Debug)]
182252
pub struct Country<'a> {
253+
#[serde(skip_serializing_if = "Option::is_none")]
183254
pub confidence: Option<u8>,
255+
#[serde(skip_serializing_if = "Option::is_none")]
184256
pub geoname_id: Option<u32>,
257+
#[serde(skip_serializing_if = "Option::is_none")]
185258
pub is_in_european_union: Option<bool>,
259+
#[serde(skip_serializing_if = "Option::is_none")]
186260
pub iso_code: Option<&'a str>,
261+
#[serde(skip_serializing_if = "Option::is_none")]
187262
pub names: Option<BTreeMap<&'a str, &'a str>>,
188263
}
189264

190265
#[derive(Deserialize, Serialize, Clone, Debug)]
191266
pub struct Location<'a> {
267+
#[serde(skip_serializing_if = "Option::is_none")]
192268
pub accuracy_radius: Option<u16>,
269+
#[serde(skip_serializing_if = "Option::is_none")]
193270
pub latitude: Option<f64>,
271+
#[serde(skip_serializing_if = "Option::is_none")]
194272
pub longitude: Option<f64>,
273+
#[serde(skip_serializing_if = "Option::is_none")]
195274
pub metro_code: Option<u16>,
275+
#[serde(skip_serializing_if = "Option::is_none")]
196276
pub time_zone: Option<&'a str>,
197277
}
198278

199279
#[derive(Deserialize, Serialize, Clone, Debug)]
200280
pub struct Postal<'a> {
281+
#[serde(skip_serializing_if = "Option::is_none")]
201282
pub code: Option<&'a str>,
283+
#[serde(skip_serializing_if = "Option::is_none")]
202284
pub confidence: Option<u8>,
203285
}
204286

205287
#[derive(Deserialize, Serialize, Clone, Debug)]
206288
pub struct Subdivision<'a> {
289+
#[serde(skip_serializing_if = "Option::is_none")]
207290
pub confidence: Option<u8>,
291+
#[serde(skip_serializing_if = "Option::is_none")]
208292
pub geoname_id: Option<u32>,
293+
#[serde(skip_serializing_if = "Option::is_none")]
209294
pub iso_code: Option<&'a str>,
295+
#[serde(skip_serializing_if = "Option::is_none")]
210296
pub names: Option<BTreeMap<&'a str, &'a str>>,
211297
}
212298

213299
#[derive(Deserialize, Serialize, Clone, Debug)]
214300
pub struct Traits<'a> {
301+
#[serde(skip_serializing_if = "Option::is_none")]
215302
pub autonomous_system_number: Option<u32>,
303+
#[serde(skip_serializing_if = "Option::is_none")]
216304
pub autonomous_system_organization: Option<&'a str>,
305+
#[serde(skip_serializing_if = "Option::is_none")]
217306
pub connection_type: Option<&'a str>,
307+
#[serde(skip_serializing_if = "Option::is_none")]
218308
pub domain: Option<&'a str>,
309+
#[serde(skip_serializing_if = "Option::is_none")]
219310
pub is_anonymous: Option<bool>,
311+
#[serde(skip_serializing_if = "Option::is_none")]
220312
pub is_anonymous_proxy: Option<bool>,
313+
#[serde(skip_serializing_if = "Option::is_none")]
221314
pub is_anonymous_vpn: Option<bool>,
315+
#[serde(skip_serializing_if = "Option::is_none")]
222316
pub is_anycast: Option<bool>,
317+
#[serde(skip_serializing_if = "Option::is_none")]
223318
pub is_hosting_provider: Option<bool>,
319+
#[serde(skip_serializing_if = "Option::is_none")]
224320
pub isp: Option<&'a str>,
321+
#[serde(skip_serializing_if = "Option::is_none")]
225322
pub is_public_proxy: Option<bool>,
323+
#[serde(skip_serializing_if = "Option::is_none")]
226324
pub is_residential_proxy: Option<bool>,
325+
#[serde(skip_serializing_if = "Option::is_none")]
227326
pub is_satellite_provider: Option<bool>,
327+
#[serde(skip_serializing_if = "Option::is_none")]
228328
pub is_tor_exit_node: Option<bool>,
329+
#[serde(skip_serializing_if = "Option::is_none")]
229330
pub mobile_country_code: Option<&'a str>,
331+
#[serde(skip_serializing_if = "Option::is_none")]
230332
pub mobile_network_code: Option<&'a str>,
333+
#[serde(skip_serializing_if = "Option::is_none")]
231334
pub organization: Option<&'a str>,
335+
#[serde(skip_serializing_if = "Option::is_none")]
232336
pub user_type: Option<&'a str>,
233337
}
234338
}

src/maxminddb/reader_test.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::net::IpAddr;
22
use std::str::FromStr;
33

44
use serde::Deserialize;
5+
use serde_json::json;
56

67
use super::{MaxMindDBError, Reader};
78

@@ -483,3 +484,23 @@ fn check_ip<T: AsRef<[u8]>>(reader: &Reader<T>, ip_version: usize) {
483484
}
484485
}
485486
}
487+
488+
#[test]
489+
fn test_json_serialize() {
490+
use super::geoip2::City;
491+
let _ = env_logger::try_init();
492+
493+
let filename = "test-data/test-data/GeoIP2-City-Test.mmdb";
494+
495+
let reader = Reader::open_readfile(filename).unwrap();
496+
497+
let ip: IpAddr = FromStr::from_str("89.160.20.112").unwrap();
498+
let city: City = reader.lookup(ip).unwrap();
499+
500+
let json_string = json!(city).to_string();
501+
502+
assert_eq!(
503+
json_string,
504+
r#"{"city":{"geoname_id":2694762,"names":{"de":"Linköping","en":"Linköping","fr":"Linköping","ja":"リンシェーピング","zh-CN":"林雪平"}},"continent":{"code":"EU","geoname_id":6255148,"names":{"de":"Europa","en":"Europe","es":"Europa","fr":"Europe","ja":"ヨーロッパ","pt-BR":"Europa","ru":"Европа","zh-CN":"欧洲"}},"country":{"geoname_id":2661886,"is_in_european_union":true,"iso_code":"SE","names":{"de":"Schweden","en":"Sweden","es":"Suecia","fr":"Suède","ja":"スウェーデン王国","pt-BR":"Suécia","ru":"Швеция","zh-CN":"瑞典"}},"location":{"accuracy_radius":76,"latitude":58.4167,"longitude":15.6167,"time_zone":"Europe/Stockholm"},"registered_country":{"geoname_id":2921044,"is_in_european_union":true,"iso_code":"DE","names":{"de":"Deutschland","en":"Germany","es":"Alemania","fr":"Allemagne","ja":"ドイツ連邦共和国","pt-BR":"Alemanha","ru":"Германия","zh-CN":"德国"}},"subdivisions":[{"geoname_id":2685867,"iso_code":"E","names":{"en":"Östergötland County","fr":"Comté d'Östergötland"}}]}"#
505+
);
506+
}

0 commit comments

Comments
 (0)