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}