geojson/de.rs
1//!
2//! To build instances of your struct from a GeoJSON String or reader, your type *must*
3//! implement or derive [`serde::Deserialize`]:
4//!
5//! ```rust, ignore
6//! #[derive(serde::Deserialize)]
7//! struct MyStruct {
8//! ...
9//! }
10//! ```
11//!
12//! Your type *must* have a field called `geometry` and it must be `deserialize_with` [`deserialize_geometry`](crate::de::deserialize_geometry):
13//! ```rust, ignore
14//! #[derive(serde::Deserialize)]
15//! struct MyStruct {
16//! #[serde(deserialize_with = "geojson::de::deserialize_geometry")]
17//! geometry: geo_types::Point<f64>,
18//! ...
19//! }
20//! ```
21//!
22//! All fields in your struct other than `geometry` will be deserialized from the `properties` of the
23//! GeoJSON Feature.
24//!
25//! # Examples
26#![cfg_attr(feature = "geo-types", doc = "```")]
27#![cfg_attr(not(feature = "geo-types"), doc = "```ignore")]
28//! use serde::Deserialize;
29//! use geojson::de::deserialize_geometry;
30//!
31//! #[derive(Deserialize)]
32//! struct MyStruct {
33//! // Deserialize from GeoJSON, rather than expecting the type's default serialization
34//! #[serde(deserialize_with = "deserialize_geometry")]
35//! geometry: geo_types::Point<f64>,
36//! name: String,
37//! population: u64
38//! }
39//!
40//! let input_geojson = serde_json::json!(
41//! {
42//! "type":"FeatureCollection",
43//! "features": [
44//! {
45//! "type": "Feature",
46//! "geometry": { "coordinates": [11.1,22.2], "type": "Point" },
47//! "properties": {
48//! "name": "Downtown",
49//! "population": 123
50//! }
51//! },
52//! {
53//! "type": "Feature",
54//! "geometry": { "coordinates": [33.3, 44.4], "type": "Point" },
55//! "properties": {
56//! "name": "Uptown",
57//! "population": 456
58//! }
59//! }
60//! ]
61//! }
62//! ).to_string();
63//!
64//! let my_structs: Vec<MyStruct> = geojson::de::deserialize_feature_collection_str_to_vec(&input_geojson).unwrap();
65//! assert_eq!("Downtown", my_structs[0].name);
66//! assert_eq!(11.1, my_structs[0].geometry.x());
67//!
68//! assert_eq!("Uptown", my_structs[1].name);
69//! assert_eq!(33.3, my_structs[1].geometry.x());
70//! ```
71//!
72//! # Reading *and* Writing GeoJSON
73//!
74//! This module is only concerned with _reading in_ GeoJSON. If you'd also like to write GeoJSON
75//! output, you'll want to combine this with the functionality from the [`crate::ser`] module:
76//! ```ignore
77//! #[derive(serde::Serialize, serde::Deserialize)]
78//! struct MyStruct {
79//! // Serialize as GeoJSON, rather than using the type's default serialization
80//! #[serde(serialize_with = "serialize_geometry", deserialize_with = "deserialize_geometry")]
81//! geometry: geo_types::Point<f64>,
82//! ...
83//! }
84//! ```
85use crate::{Feature, FeatureReader, JsonValue, Result};
86
87use std::convert::{TryFrom, TryInto};
88use std::fmt::Formatter;
89use std::io::Read;
90use std::marker::PhantomData;
91
92use serde::de::{Deserialize, Deserializer, Error, IntoDeserializer};
93
94/// Deserialize a GeoJSON FeatureCollection into your custom structs.
95///
96/// Your struct must implement or derive `serde::Deserialize`.
97///
98/// You must use the [`deserialize_geometry`] helper if you are using geo_types or some other geometry
99/// representation other than geojson::Geometry.
100///
101/// # Examples
102#[cfg_attr(feature = "geo-types", doc = "```")]
103#[cfg_attr(not(feature = "geo-types"), doc = "```ignore")]
104/// use serde::Deserialize;
105/// use geojson::de::deserialize_geometry;
106///
107/// #[derive(Deserialize)]
108/// struct MyStruct {
109/// // You must use the `deserialize_geometry` helper if you are using geo_types or some other
110/// // geometry representation other than geojson::Geometry
111/// #[serde(deserialize_with = "deserialize_geometry")]
112/// geometry: geo_types::Point<f64>,
113/// name: String,
114/// }
115///
116/// let feature_collection_str = r#"{
117/// "type": "FeatureCollection",
118/// "features": [
119/// {
120/// "type": "Feature",
121/// "geometry": { "type": "Point", "coordinates": [11.1, 22.2] },
122/// "properties": { "name": "Downtown" }
123/// },
124/// {
125/// "type": "Feature",
126/// "geometry": { "type": "Point", "coordinates": [33.3, 44.4] },
127/// "properties": { "name": "Uptown" }
128/// }
129/// ]
130/// }"#;
131/// let reader = feature_collection_str.as_bytes();
132///
133/// // enumerate over the features in the feature collection
134/// for (idx, feature_result) in geojson::de::deserialize_feature_collection::<MyStruct>(reader).unwrap().enumerate() {
135/// let my_struct = feature_result.expect("valid geojson for MyStruct");
136/// if idx == 0 {
137/// assert_eq!(my_struct.name, "Downtown");
138/// assert_eq!(my_struct.geometry.x(), 11.1);
139/// } else if idx == 1 {
140/// assert_eq!(my_struct.name, "Uptown");
141/// assert_eq!(my_struct.geometry.x(), 33.3);
142/// } else {
143/// unreachable!("there are only two features in this collection");
144/// }
145/// }
146/// ```
147pub fn deserialize_feature_collection<'de, T>(
148 feature_collection_reader: impl Read,
149) -> Result<impl Iterator<Item = Result<T>>>
150where
151 T: Deserialize<'de>,
152{
153 #[allow(deprecated)]
154 let iter = crate::FeatureIterator::new(feature_collection_reader).map(
155 |feature_value: Result<JsonValue>| {
156 let deserializer = feature_value?.into_deserializer();
157 let visitor = FeatureVisitor::new();
158 let record: T = deserializer.deserialize_map(visitor)?;
159
160 Ok(record)
161 },
162 );
163 Ok(iter)
164}
165
166/// Build a `Vec` of structs from a GeoJson `&str`.
167///
168/// See [`deserialize_feature_collection`] for more.
169pub fn deserialize_feature_collection_str_to_vec<'de, T>(
170 feature_collection_str: &str,
171) -> Result<Vec<T>>
172where
173 T: Deserialize<'de>,
174{
175 let feature_collection_reader = feature_collection_str.as_bytes();
176 deserialize_feature_collection(feature_collection_reader)?.collect()
177}
178
179/// Build a `Vec` of structs from a GeoJson reader.
180///
181/// See [`deserialize_feature_collection`] for more.
182pub fn deserialize_feature_collection_to_vec<'de, T>(
183 feature_collection_reader: impl Read,
184) -> Result<Vec<T>>
185where
186 T: Deserialize<'de>,
187{
188 deserialize_feature_collection(feature_collection_reader)?.collect()
189}
190
191/// [`serde::deserialize_with`](https://serde.rs/field-attrs.html#deserialize_with) helper to deserialize a GeoJSON Geometry into another type, like a
192/// [`geo_types`] Geometry.
193///
194/// # Examples
195#[cfg_attr(feature = "geo-types", doc = "```")]
196#[cfg_attr(not(feature = "geo-types"), doc = "```ignore")]
197/// use serde::Deserialize;
198/// use geojson::de::deserialize_geometry;
199///
200/// #[derive(Deserialize)]
201/// struct MyStruct {
202/// #[serde(deserialize_with = "deserialize_geometry")]
203/// geometry: geo_types::Point<f64>,
204/// name: String,
205/// }
206///
207/// let feature_collection_str = r#"{
208/// "type": "FeatureCollection",
209/// "features": [
210/// {
211/// "type": "Feature",
212/// "geometry": { "type": "Point", "coordinates": [11.1, 22.2] },
213/// "properties": { "name": "Downtown" }
214/// },
215/// {
216/// "type": "Feature",
217/// "geometry": { "type": "Point", "coordinates": [33.3, 44.4] },
218/// "properties": { "name": "Uptown" }
219/// }
220/// ]
221/// }"#;
222///
223/// let features: Vec<MyStruct> = geojson::de::deserialize_feature_collection_str_to_vec(feature_collection_str).unwrap();
224///
225/// assert_eq!(features[0].name, "Downtown");
226/// assert_eq!(features[0].geometry.x(), 11.1);
227/// ```
228pub fn deserialize_geometry<'de, D, G>(deserializer: D) -> std::result::Result<G, D::Error>
229where
230 D: Deserializer<'de>,
231 G: TryFrom<crate::Geometry>,
232 G::Error: std::fmt::Display,
233{
234 let geojson_geometry = crate::Geometry::deserialize(deserializer)?;
235 geojson_geometry
236 .try_into()
237 .map_err(deserialize_error_msg::<D>)
238}
239
240/// [`serde::deserialize_with`](https://serde.rs/field-attrs.html#deserialize_with) helper to deserialize an optional GeoJSON Geometry into another type,
241/// like an optional [`geo_types`] Geometry.
242///
243/// # Examples
244#[cfg_attr(feature = "geo-types", doc = "```")]
245#[cfg_attr(not(feature = "geo-types"), doc = "```ignore")]
246/// use geojson::de::deserialize_optional_geometry;
247/// use serde::Deserialize;
248/// use serde_json::{json, from_value};
249///
250/// #[derive(Deserialize)]
251/// struct MyStruct {
252/// #[serde(rename = "count")]
253/// _count: usize,
254/// #[serde(default, deserialize_with = "deserialize_optional_geometry")]
255/// geometry: Option<geo_types::Point<f64>>,
256/// }
257///
258/// let json = json! {{
259/// "count": 0,
260/// "geometry": {
261/// "type": "Point",
262/// "coordinates": [125.6, 10.1]
263/// },
264/// }};
265/// let feature: MyStruct = from_value(json).unwrap();
266/// assert!(feature.geometry.is_some());
267///
268/// let json = json! {{
269/// "count": 1,
270/// }};
271/// let feature: MyStruct = from_value(json).unwrap();
272/// assert!(feature.geometry.is_none())
273/// ```
274pub fn deserialize_optional_geometry<'de, D, G>(
275 deserializer: D,
276) -> std::result::Result<Option<G>, D::Error>
277where
278 D: Deserializer<'de>,
279 G: TryFrom<crate::Geometry>,
280 G::Error: std::fmt::Display,
281{
282 Option::<crate::Geometry>::deserialize(deserializer)?
283 .map(TryInto::try_into)
284 .transpose()
285 .map_err(deserialize_error_msg::<D>)
286}
287
288fn deserialize_error_msg<'de, D: Deserializer<'de>>(
289 error: impl std::fmt::Display,
290) -> <D as serde::Deserializer<'de>>::Error {
291 Error::custom(format!(
292 "unable to convert from geojson Geometry: {}",
293 error
294 ))
295}
296
297/// Deserialize a GeoJSON FeatureCollection into [`Feature`] structs.
298///
299/// If instead you'd like to deserialize your own structs from GeoJSON, see [`deserialize_feature_collection`].
300pub fn deserialize_features_from_feature_collection(
301 feature_collection_reader: impl Read,
302) -> impl Iterator<Item = Result<Feature>> {
303 FeatureReader::from_reader(feature_collection_reader).features()
304}
305
306/// Deserialize a single GeoJSON Feature into your custom struct.
307///
308/// It's more common to deserialize a FeatureCollection than a single feature. If you're looking to
309/// do that, see [`deserialize_feature_collection`] instead.
310///
311/// Your struct must implement or derive `serde::Deserialize`.
312///
313/// # Examples
314#[cfg_attr(feature = "geo-types", doc = "```")]
315#[cfg_attr(not(feature = "geo-types"), doc = "```ignore")]
316/// use serde::Deserialize;
317/// use geojson::de::deserialize_geometry;
318///
319/// #[derive(Deserialize)]
320/// struct MyStruct {
321/// // You must use the `deserialize_geometry` helper if you are using geo_types or some other
322/// // geometry representation other than geojson::Geometry
323/// #[serde(deserialize_with = "deserialize_geometry")]
324/// geometry: geo_types::Point<f64>,
325/// name: String,
326/// }
327///
328/// let feature_str = r#"{
329/// "type": "Feature",
330/// "geometry": { "type": "Point", "coordinates": [11.1, 22.2] },
331/// "properties": { "name": "Downtown" }
332/// }"#;
333/// let reader = feature_str.as_bytes();
334///
335/// // build your struct from GeoJSON
336/// let my_struct = geojson::de::deserialize_single_feature::<MyStruct>(reader).expect("valid geojson for MyStruct");
337///
338/// assert_eq!(my_struct.name, "Downtown");
339/// assert_eq!(my_struct.geometry.x(), 11.1);
340/// ```
341pub fn deserialize_single_feature<'de, T>(feature_reader: impl Read) -> Result<T>
342where
343 T: Deserialize<'de>,
344{
345 let feature_value: JsonValue = serde_json::from_reader(feature_reader)?;
346 let deserializer = feature_value.into_deserializer();
347 let visitor = FeatureVisitor::new();
348 Ok(deserializer.deserialize_map(visitor)?)
349}
350
351/// Interpret a [`Feature`] as an instance of type `T`.
352///
353/// This is analogous to [`serde_json::from_value`](https://docs.rs/serde_json/latest/serde_json/fn.from_value.html)
354///
355/// `T`'s `geometry` field will be deserialized from `feature.geometry`.
356/// All other fields will be deserialized from `feature.properties`.
357///
358/// # Examples
359#[cfg_attr(feature = "geo-types", doc = "```")]
360#[cfg_attr(not(feature = "geo-types"), doc = "```ignore")]
361/// use serde::Deserialize;
362/// use geojson::Feature;
363/// use geojson::de::{from_feature, deserialize_geometry, deserialize_single_feature};
364/// use std::str::FromStr;
365///
366/// #[derive(Deserialize)]
367/// struct MyStruct {
368/// // Deserialize `geometry` as GeoJSON, rather than using the type's default deserialization
369/// #[serde(deserialize_with = "deserialize_geometry")]
370/// geometry: geo_types::Point,
371/// name: String,
372/// }
373///
374/// let geojson_str = r#"{
375/// "type": "Feature",
376/// "geometry": { "type": "Point", "coordinates": [1.0, 2.0] },
377/// "properties": {
378/// "name": "My Name"
379/// }
380/// }"#;
381/// let feature = Feature::from_str(geojson_str).unwrap();
382///
383/// let my_struct: MyStruct = from_feature(feature).unwrap();
384///
385/// assert_eq!("My Name", my_struct.name);
386/// assert_eq!(geo_types::Point::new(1.0, 2.0), my_struct.geometry);
387/// ```
388///
389/// # Errors
390///
391/// Deserialization can fail if `T`'s implementation of `Deserialize` decides to fail.
392pub fn from_feature<'de, T>(feature: Feature) -> Result<T>
393where
394 T: Deserialize<'de>,
395{
396 let feature_value: JsonValue = serde_json::to_value(feature)?;
397 let deserializer = feature_value.into_deserializer();
398 let visitor = FeatureVisitor::new();
399 Ok(deserializer.deserialize_map(visitor)?)
400}
401
402struct FeatureVisitor<D> {
403 _marker: PhantomData<D>,
404}
405
406impl<D> FeatureVisitor<D> {
407 fn new() -> Self {
408 Self {
409 _marker: PhantomData,
410 }
411 }
412}
413
414impl<'de, D> serde::de::Visitor<'de> for FeatureVisitor<D>
415where
416 D: Deserialize<'de>,
417{
418 type Value = D;
419
420 fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
421 write!(formatter, "a valid GeoJSON Feature object")
422 }
423
424 fn visit_map<A>(self, mut map_access: A) -> std::result::Result<Self::Value, A::Error>
425 where
426 A: serde::de::MapAccess<'de>,
427 {
428 let mut has_feature_type = false;
429 use std::collections::HashMap;
430 let mut hash_map: HashMap<String, JsonValue> = HashMap::new();
431
432 while let Some((key, value)) = map_access.next_entry::<String, JsonValue>()? {
433 if key == "type" {
434 if value.as_str() == Some("Feature") {
435 has_feature_type = true;
436 } else {
437 return Err(Error::custom(
438 "GeoJSON Feature had a `type` other than \"Feature\"",
439 ));
440 }
441 } else if key == "geometry" {
442 if let JsonValue::Object(_) = value {
443 hash_map.insert("geometry".to_string(), value);
444 } else {
445 return Err(Error::custom("GeoJSON Feature had a unexpected geometry"));
446 }
447 } else if key == "properties" {
448 if let JsonValue::Object(properties) = value {
449 // flatten properties onto struct
450 for (prop_key, prop_value) in properties {
451 hash_map.insert(prop_key, prop_value);
452 }
453 } else {
454 return Err(Error::custom("GeoJSON Feature had a unexpected geometry"));
455 }
456 } else {
457 log::debug!("foreign members are not handled by Feature deserializer")
458 }
459 }
460
461 if has_feature_type {
462 let d2 = hash_map.into_deserializer();
463 let result =
464 Deserialize::deserialize(d2).map_err(|e| Error::custom(format!("{}", e)))?;
465 Ok(result)
466 } else {
467 Err(Error::custom(
468 "A GeoJSON Feature must have a `type: \"Feature\"` field, but found none.",
469 ))
470 }
471 }
472}
473
474#[cfg(test)]
475pub(crate) mod tests {
476 use super::*;
477
478 use crate::{JsonObject, JsonValue};
479
480 use serde_json::json;
481
482 pub(crate) fn feature_collection() -> JsonValue {
483 json!({
484 "type": "FeatureCollection",
485 "features": [
486 {
487 "type": "Feature",
488 "geometry": {
489 "type": "Point",
490 "coordinates": [125.6, 10.1]
491 },
492 "properties": {
493 "name": "Dinagat Islands",
494 "age": 123
495 }
496 },
497 {
498 "type": "Feature",
499 "geometry": {
500 "type": "Point",
501 "coordinates": [2.3, 4.5]
502 },
503 "properties": {
504 "name": "Neverland",
505 "age": 456
506 }
507 }
508 ]
509 })
510 }
511
512 #[test]
513 fn test_deserialize_feature_collection() {
514 use crate::Feature;
515
516 let feature_collection_string = feature_collection().to_string();
517 let bytes_reader = feature_collection_string.as_bytes();
518
519 let records: Vec<Feature> = deserialize_features_from_feature_collection(bytes_reader)
520 .map(|feature_result: Result<Feature>| feature_result.unwrap())
521 .collect();
522
523 assert_eq!(records.len(), 2);
524 let first_age = {
525 let props = records.first().unwrap().properties.as_ref().unwrap();
526 props.get("age").unwrap().as_i64().unwrap()
527 };
528 assert_eq!(first_age, 123);
529
530 let second_age = {
531 let props = records.get(1).unwrap().properties.as_ref().unwrap();
532 props.get("age").unwrap().as_i64().unwrap()
533 };
534 assert_eq!(second_age, 456);
535 }
536
537 #[cfg(feature = "geo-types")]
538 mod geo_types_tests {
539 use super::*;
540
541 use serde::Deserialize;
542
543 #[test]
544 fn geometry_field() {
545 #[derive(Deserialize)]
546 struct MyStruct {
547 #[serde(deserialize_with = "deserialize_geometry")]
548 geometry: geo_types::Geometry<f64>,
549 name: String,
550 age: u64,
551 }
552
553 let feature_collection_string = feature_collection().to_string();
554 let bytes_reader = feature_collection_string.as_bytes();
555
556 let records: Vec<MyStruct> = deserialize_feature_collection(bytes_reader)
557 .expect("a valid feature collection")
558 .collect::<Result<Vec<_>>>()
559 .expect("valid features");
560
561 assert_eq!(records.len(), 2);
562
563 assert_eq!(
564 records[0].geometry,
565 geo_types::point!(x: 125.6, y: 10.1).into()
566 );
567 assert_eq!(records[0].name, "Dinagat Islands");
568 assert_eq!(records[0].age, 123);
569
570 assert_eq!(
571 records[1].geometry,
572 geo_types::point!(x: 2.3, y: 4.5).into()
573 );
574 assert_eq!(records[1].name, "Neverland");
575 assert_eq!(records[1].age, 456);
576 }
577
578 #[test]
579 fn specific_geometry_variant_field() {
580 #[derive(Deserialize)]
581 struct MyStruct {
582 #[serde(deserialize_with = "deserialize_geometry")]
583 geometry: geo_types::Point<f64>,
584 name: String,
585 age: u64,
586 }
587
588 let feature_collection_string = feature_collection().to_string();
589 let bytes_reader = feature_collection_string.as_bytes();
590
591 let records: Vec<MyStruct> = deserialize_feature_collection(bytes_reader)
592 .expect("a valid feature collection")
593 .collect::<Result<Vec<_>>>()
594 .expect("valid features");
595
596 assert_eq!(records.len(), 2);
597
598 assert_eq!(records[0].geometry, geo_types::point!(x: 125.6, y: 10.1));
599 assert_eq!(records[0].name, "Dinagat Islands");
600 assert_eq!(records[0].age, 123);
601
602 assert_eq!(records[1].geometry, geo_types::point!(x: 2.3, y: 4.5));
603 assert_eq!(records[1].name, "Neverland");
604 assert_eq!(records[1].age, 456);
605 }
606
607 #[test]
608 fn wrong_geometry_variant_field() {
609 #[allow(unused)]
610 #[derive(Deserialize)]
611 struct MyStruct {
612 #[serde(deserialize_with = "deserialize_geometry")]
613 geometry: geo_types::LineString<f64>,
614 name: String,
615 age: u64,
616 }
617
618 let feature_collection_string = feature_collection().to_string();
619 let bytes_reader = feature_collection_string.as_bytes();
620
621 let records: Vec<Result<MyStruct>> = deserialize_feature_collection(bytes_reader)
622 .unwrap()
623 .collect();
624 assert_eq!(records.len(), 2);
625 assert!(records[0].is_err());
626 assert!(records[1].is_err());
627
628 let err = match records[0].as_ref() {
629 Ok(_ok) => panic!("expected Err, but found OK"),
630 Err(e) => e,
631 };
632
633 // This will fail if we update our error text, but I wanted to show that the error text
634 // is reasonably discernible.
635 let expected_err_text = r#"Error while deserializing JSON: unable to convert from geojson Geometry: Expected type: `LineString`, but found `Point`"#;
636 assert_eq!(err.to_string(), expected_err_text);
637 }
638
639 #[test]
640 fn deserializes_optional_point() {
641 #[derive(serde::Deserialize)]
642 struct MyStruct {
643 #[serde(rename = "count")]
644 _count: usize,
645 #[serde(default, deserialize_with = "deserialize_optional_geometry")]
646 geometry: Option<geo_types::Point<f64>>,
647 }
648
649 let json = json! {{
650 "count": 0,
651 "geometry": {
652 "type": "Point",
653 "coordinates": [125.6, 10.1]
654 },
655 }};
656 let feature: MyStruct = serde_json::from_value(json).unwrap();
657 assert!(feature.geometry.is_some());
658
659 let json = json! {{
660 "count": 1,
661 }};
662 let feature: MyStruct = serde_json::from_value(json).unwrap();
663 assert!(feature.geometry.is_none())
664 }
665
666 #[test]
667 fn test_from_feature() {
668 #[derive(Debug, PartialEq, Deserialize)]
669 struct MyStruct {
670 #[serde(deserialize_with = "deserialize_geometry")]
671 geometry: geo_types::Point<f64>,
672 name: String,
673 age: u64,
674 }
675
676 let feature = Feature {
677 bbox: None,
678 geometry: Some(crate::Geometry::new(crate::Value::Point(vec![125.6, 10.1]))),
679 id: None,
680 properties: Some(
681 json!({
682 "name": "Dinagat Islands",
683 "age": 123,
684 })
685 .as_object()
686 .unwrap()
687 .clone(),
688 ),
689 foreign_members: None,
690 };
691
692 let actual: MyStruct = from_feature(feature).unwrap();
693 let expected = MyStruct {
694 geometry: geo_types::Point::new(125.6, 10.1),
695 name: "Dinagat Islands".to_string(),
696 age: 123,
697 };
698 assert_eq!(actual, expected);
699 }
700 }
701
702 #[cfg(feature = "geo-types")]
703 #[test]
704 fn roundtrip() {
705 use crate::ser::serialize_geometry;
706 use serde::{Deserialize, Serialize};
707
708 #[derive(Serialize, Deserialize)]
709 struct MyStruct {
710 #[serde(
711 serialize_with = "serialize_geometry",
712 deserialize_with = "deserialize_geometry"
713 )]
714 geometry: geo_types::Point<f64>,
715 name: String,
716 age: u64,
717 }
718
719 let feature_collection_string = feature_collection().to_string();
720 let bytes_reader = feature_collection_string.as_bytes();
721
722 let mut elements = deserialize_feature_collection_to_vec::<MyStruct>(bytes_reader).unwrap();
723 for element in &mut elements {
724 element.age += 1;
725 element.geometry.set_x(element.geometry.x() + 1.0);
726 }
727 let actual_output = crate::ser::to_feature_collection_string(&elements).unwrap();
728
729 use std::str::FromStr;
730 let actual_output_json = JsonValue::from_str(&actual_output).unwrap();
731 let expected_output_json = json!({
732 "type": "FeatureCollection",
733 "features": [
734 {
735 "type": "Feature",
736 "geometry": {
737 "type": "Point",
738 "coordinates": [126.6, 10.1]
739 },
740 "properties": {
741 "name": "Dinagat Islands",
742 "age": 124
743 }
744 },
745 {
746 "type": "Feature",
747 "geometry": {
748 "type": "Point",
749 "coordinates": [3.3, 4.5]
750 },
751 "properties": {
752 "name": "Neverland",
753 "age": 457
754 }
755 }
756 ]
757 });
758
759 assert_eq!(actual_output_json, expected_output_json);
760 }
761
762 #[cfg(feature = "geo-types")]
763 #[test]
764 fn all_props() {
765 use crate::ser::serialize_geometry;
766 use serde::{Deserialize, Serialize};
767
768 #[derive(Serialize, Deserialize)]
769 struct MyStruct {
770 #[serde(
771 serialize_with = "serialize_geometry",
772 deserialize_with = "deserialize_geometry"
773 )]
774 geometry: geo_types::Point<f64>,
775
776 #[serde(flatten)]
777 properties: JsonObject,
778 }
779
780 let feature_collection_string = feature_collection().to_string();
781 let bytes_reader = feature_collection_string.as_bytes();
782
783 let mut elements = deserialize_feature_collection_to_vec::<MyStruct>(bytes_reader).unwrap();
784 for element in &mut elements {
785 // dbg!(&element.properties);
786 // => [src/de.rs:615] &element.properties = {
787 // "age": Number(123),
788 // "name": String("Dinagat Islands"),
789 // }
790 let mut age = element
791 .properties
792 .get("age")
793 .expect("key to exist")
794 .as_u64()
795 .expect("numeric");
796 age += 1;
797 *element.properties.get_mut("age").expect("key to exist") = age.into();
798 element.geometry.set_x(element.geometry.x() + 1.0);
799 }
800 let actual_output = crate::ser::to_feature_collection_string(&elements).unwrap();
801
802 use std::str::FromStr;
803 let actual_output_json = JsonValue::from_str(&actual_output).unwrap();
804 let expected_output_json = json!({
805 "type": "FeatureCollection",
806 "features": [
807 {
808 "type": "Feature",
809 "geometry": {
810 "type": "Point",
811 "coordinates": [126.6, 10.1]
812 },
813 "properties": {
814 "name": "Dinagat Islands",
815 "age": 124
816 }
817 },
818 {
819 "type": "Feature",
820 "geometry": {
821 "type": "Point",
822 "coordinates": [3.3, 4.5]
823 },
824 "properties": {
825 "name": "Neverland",
826 "age": 457
827 }
828 }
829 ]
830 });
831
832 assert_eq!(actual_output_json, expected_output_json);
833 }
834}