geojson/ser.rs
1//!
2//! To output your struct to GeoJSON, either as a String, bytes, or to a file, your type *must*
3//! implement or derive [`serde::Serialize`]:
4//!
5//! ```rust, ignore
6//! #[derive(serde::Serialize)]
7//! struct MyStruct {
8//! ...
9//! }
10//! ```
11//!
12//! Your type *must* have a field called `geometry` and it must be `serialize_with` [`serialize_geometry`](crate::ser::serialize_geometry):
13//! ```rust, ignore
14//! #[derive(serde::Serialize)]
15//! struct MyStruct {
16//! #[serde(serialize_with = "geojson::ser::serialize_geometry")]
17//! geometry: geo_types::Point<f64>,
18//! ...
19//! }
20//! ```
21//!
22//! All fields in your struct other than `geometry` will be serialized as `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::Serialize;
29//! use geojson::ser::serialize_geometry;
30//!
31//! #[derive(Serialize)]
32//! struct MyStruct {
33//! // Serialize as geojson, rather than using the type's default serialization
34//! #[serde(serialize_with = "serialize_geometry")]
35//! geometry: geo_types::Point<f64>,
36//! name: String,
37//! population: u64
38//! }
39//!
40//! let my_structs = vec![
41//! MyStruct {
42//! geometry: geo_types::Point::new(11.1, 22.2),
43//! name: "Downtown".to_string(),
44//! population: 123
45//! },
46//! MyStruct {
47//! geometry: geo_types::Point::new(33.3, 44.4),
48//! name: "Uptown".to_string(),
49//! population: 456
50//! }
51//! ];
52//!
53//! let output_geojson = geojson::ser::to_feature_collection_string(&my_structs).unwrap();
54//!
55//! let expected_geojson = serde_json::json!(
56//! {
57//! "type":"FeatureCollection",
58//! "features": [
59//! {
60//! "type": "Feature",
61//! "geometry": { "coordinates": [11.1,22.2], "type": "Point" },
62//! "properties": {
63//! "name": "Downtown",
64//! "population": 123
65//! }
66//! },
67//! {
68//! "type": "Feature",
69//! "geometry": { "coordinates": [33.3, 44.4], "type": "Point" },
70//! "properties": {
71//! "name": "Uptown",
72//! "population": 456
73//! }
74//! }
75//! ]
76//! }
77//! );
78//! #
79//! # // re-parse the json to do a structural comparison, rather than worry about formatting
80//! # // or other meaningless deviations in an exact String comparison.
81//! # let output_geojson: serde_json::Value = serde_json::from_str(&output_geojson).unwrap();
82//! #
83//! # assert_eq!(output_geojson, expected_geojson);
84//! ```
85//!
86//! # Reading *and* Writing GeoJSON
87//!
88//! This module is only concerned with Writing out GeoJSON. If you'd also like to read GeoJSON,
89//! you'll want to combine this with the functionality from the [`crate::de`] module:
90//! ```ignore
91//! #[derive(serde::Serialize, serde::Deserialize)]
92//! struct MyStruct {
93//! // Serialize as GeoJSON, rather than using the type's default serialization
94//! #[serde(serialize_with = "serialize_geometry", deserialize_with = "deserialize_geometry")]
95//! geometry: geo_types::Point<f64>,
96//! ...
97//! }
98//! ```
99use crate::{Feature, JsonObject, JsonValue, Result};
100
101use serde::{ser::Error, Serialize, Serializer};
102
103use crate::util::expect_owned_object;
104use std::convert::TryFrom;
105use std::{convert::TryInto, io};
106
107/// Serialize a single data structure to a GeoJSON Feature string.
108///
109/// Note that `T` must have a column called `geometry`.
110///
111/// See [`to_feature_collection_string`] if instead you'd like to serialize multiple features to a
112/// FeatureCollection.
113///
114/// # Errors
115///
116/// Serialization can fail if `T`'s implementation of `Serialize` decides to
117/// fail, or if `T` contains a map with non-string keys.
118pub fn to_feature_string<T>(value: &T) -> Result<String>
119where
120 T: Serialize,
121{
122 let vec = to_feature_byte_vec(value)?;
123 let string = unsafe {
124 // We do not emit invalid UTF-8.
125 String::from_utf8_unchecked(vec)
126 };
127 Ok(string)
128}
129
130/// Serialize elements to a GeoJSON FeatureCollection string.
131///
132/// Note that `T` must have a column called `geometry`.
133///
134/// # Errors
135///
136/// Serialization can fail if `T`'s implementation of `Serialize` decides to
137/// fail, or if `T` contains a map with non-string keys.
138pub fn to_feature_collection_string<T>(values: &[T]) -> Result<String>
139where
140 T: Serialize,
141{
142 let vec = to_feature_collection_byte_vec(values)?;
143 let string = unsafe {
144 // We do not emit invalid UTF-8.
145 String::from_utf8_unchecked(vec)
146 };
147 Ok(string)
148}
149
150/// Serialize a single data structure to a GeoJSON Feature byte vector.
151///
152/// Note that `T` must have a column called `geometry`.
153///
154/// # Errors
155///
156/// Serialization can fail if `T`'s implementation of `Serialize` decides to
157/// fail, or if `T` contains a map with non-string keys.
158pub fn to_feature_byte_vec<T>(value: &T) -> Result<Vec<u8>>
159where
160 T: Serialize,
161{
162 let mut writer = Vec::with_capacity(128);
163 to_feature_writer(&mut writer, value)?;
164 Ok(writer)
165}
166
167/// Serialize elements to a GeoJSON FeatureCollection byte vector.
168///
169/// Note that `T` must have a column called `geometry`.
170///
171/// # Errors
172///
173/// Serialization can fail if `T`'s implementation of `Serialize` decides to
174/// fail, or if `T` contains a map with non-string keys.
175pub fn to_feature_collection_byte_vec<T>(values: &[T]) -> Result<Vec<u8>>
176where
177 T: Serialize,
178{
179 let mut writer = Vec::with_capacity(128);
180 to_feature_collection_writer(&mut writer, values)?;
181 Ok(writer)
182}
183
184/// Serialize a single data structure as a GeoJSON Feature into the IO stream.
185///
186/// Note that `T` must have a column called `geometry`.
187///
188/// # Errors
189///
190/// Serialization can fail if `T`'s implementation of `Serialize` decides to
191/// fail, or if `T` contains a map with non-string keys.
192pub fn to_feature_writer<W, T>(writer: W, value: &T) -> Result<()>
193where
194 W: io::Write,
195 T: Serialize,
196{
197 let feature_serializer = FeatureWrapper::new(value);
198 let mut serializer = serde_json::Serializer::new(writer);
199 feature_serializer.serialize(&mut serializer)?;
200 Ok(())
201}
202
203/// Convert a `T` into a [`Feature`].
204///
205/// This is analogous to [`serde_json::to_value`](https://docs.rs/serde_json/latest/serde_json/fn.to_value.html)
206///
207/// Note that if (and only if) `T` has a field named `geometry`, it will be serialized to
208/// `feature.geometry`.
209///
210/// All other fields will be serialized to `feature.properties`.
211///
212/// # Examples
213#[cfg_attr(feature = "geo-types", doc = "```")]
214#[cfg_attr(not(feature = "geo-types"), doc = "```ignore")]
215/// use serde::Serialize;
216/// use geojson::{Feature, Value, Geometry};
217/// use geojson::ser::{to_feature, serialize_geometry};
218///
219/// #[derive(Serialize)]
220/// struct MyStruct {
221/// // Serialize `geometry` as geojson, rather than using the type's default serialization
222/// #[serde(serialize_with = "serialize_geometry")]
223/// geometry: geo_types::Point,
224/// name: String,
225/// }
226///
227/// let my_struct = MyStruct {
228/// geometry: geo_types::Point::new(1.0, 2.0),
229/// name: "My Name".to_string()
230/// };
231///
232/// let feature: Feature = to_feature(my_struct).unwrap();
233/// assert_eq!("My Name", feature.properties.unwrap()["name"]);
234/// assert_eq!(feature.geometry.unwrap(), Geometry::new(Value::Point(vec![1.0, 2.0])));
235/// ```
236///
237/// # Errors
238///
239/// Serialization can fail if `T`'s implementation of `Serialize` decides to
240/// fail, or if `T` contains a map with non-string keys.
241pub fn to_feature<T>(value: T) -> Result<Feature>
242where
243 T: Serialize,
244{
245 let js_value = serde_json::to_value(value)?;
246 let mut js_object = expect_owned_object(js_value)?;
247
248 let geometry = if let Some(geometry_value) = js_object.remove("geometry") {
249 Some(crate::Geometry::try_from(geometry_value)?)
250 } else {
251 None
252 };
253
254 Ok(Feature {
255 bbox: None,
256 geometry,
257 id: None,
258 properties: Some(js_object),
259 foreign_members: None,
260 })
261}
262
263/// Serialize elements as a GeoJSON FeatureCollection into the IO stream.
264///
265/// Note that `T` must have a column called `geometry`.
266///
267/// # Errors
268///
269/// Serialization can fail if `T`'s implementation of `Serialize` decides to
270/// fail, or if `T` contains a map with non-string keys.
271pub fn to_feature_collection_writer<W, T>(writer: W, features: &[T]) -> Result<()>
272where
273 W: io::Write,
274 T: Serialize,
275{
276 use serde::ser::SerializeMap;
277
278 let mut ser = serde_json::Serializer::new(writer);
279 let mut map = ser.serialize_map(Some(2))?;
280 map.serialize_entry("type", "FeatureCollection")?;
281 map.serialize_entry("features", &Features::new(features))?;
282 map.end()?;
283 Ok(())
284}
285
286/// [`serde::serialize_with`](https://serde.rs/field-attrs.html#serialize_with) helper to serialize a type like a
287/// [`geo_types`], as a GeoJSON Geometry.
288///
289/// # Examples
290#[cfg_attr(feature = "geo-types", doc = "```")]
291#[cfg_attr(not(feature = "geo-types"), doc = "```ignore")]
292/// use serde::Serialize;
293/// use geojson::ser::serialize_geometry;
294///
295/// #[derive(Serialize)]
296/// struct MyStruct {
297/// // Serialize as geojson, rather than using the type's default serialization
298/// #[serde(serialize_with = "serialize_geometry")]
299/// geometry: geo_types::Point<f64>,
300/// name: String,
301/// }
302///
303/// let my_structs = vec![
304/// MyStruct {
305/// geometry: geo_types::Point::new(11.1, 22.2),
306/// name: "Downtown".to_string()
307/// },
308/// MyStruct {
309/// geometry: geo_types::Point::new(33.3, 44.4),
310/// name: "Uptown".to_string()
311/// }
312/// ];
313///
314/// let geojson_string = geojson::ser::to_feature_collection_string(&my_structs).unwrap();
315///
316/// assert!(geojson_string.contains(r#""geometry":{"coordinates":[11.1,22.2],"type":"Point"}"#));
317/// ```
318pub fn serialize_geometry<IG, S>(geometry: IG, ser: S) -> std::result::Result<S::Ok, S::Error>
319where
320 IG: TryInto<crate::Geometry>,
321 S: serde::Serializer,
322 <IG as TryInto<crate::Geometry>>::Error: std::fmt::Display,
323{
324 geometry
325 .try_into()
326 .map_err(serialize_error_msg::<S>)?
327 .serialize(ser)
328}
329
330/// [`serde::serialize_with`](https://serde.rs/field-attrs.html#serialize_with) helper to serialize an optional type like a
331/// [`geo_types`], as an optional GeoJSON Geometry.
332///
333/// # Examples
334#[cfg_attr(feature = "geo-types", doc = "```")]
335#[cfg_attr(not(feature = "geo-types"), doc = "```ignore")]
336/// use geojson::ser::serialize_optional_geometry;
337/// use serde::Serialize;
338/// use serde_json::{json, to_value};
339///
340/// #[derive(Serialize)]
341/// struct MyStruct {
342/// count: usize,
343/// #[serde(
344/// skip_serializing_if = "Option::is_none",
345/// serialize_with = "serialize_optional_geometry"
346/// )]
347/// geometry: Option<geo_types::Point<f64>>,
348/// }
349///
350/// let my_struct = MyStruct {
351/// count: 0,
352/// geometry: Some(geo_types::Point::new(1.2, 0.5)),
353/// };
354/// let json = json! {{
355/// "count": 0,
356/// "geometry": {
357/// "type": "Point",
358/// "coordinates": [1.2, 0.5]
359/// },
360/// }};
361/// assert_eq!(json, to_value(my_struct).unwrap());
362///
363/// let my_struct = MyStruct {
364/// count: 1,
365/// geometry: None,
366/// };
367/// let json = json! {{
368/// "count": 1,
369/// }};
370/// assert_eq!(json, to_value(my_struct).unwrap());
371/// ```
372pub fn serialize_optional_geometry<'a, IG, S>(
373 geometry: &'a Option<IG>,
374 ser: S,
375) -> std::result::Result<S::Ok, S::Error>
376where
377 &'a IG: std::convert::TryInto<crate::Geometry>,
378 S: serde::Serializer,
379 <&'a IG as TryInto<crate::Geometry>>::Error: std::fmt::Display,
380{
381 geometry
382 .as_ref()
383 .map(TryInto::try_into)
384 .transpose()
385 .map_err(serialize_error_msg::<S>)?
386 .serialize(ser)
387}
388
389fn serialize_error_msg<S: Serializer>(error: impl std::fmt::Display) -> S::Error {
390 Error::custom(format!("failed to convert geometry to GeoJSON: {}", error))
391}
392
393struct Features<'a, T>
394where
395 T: Serialize,
396{
397 features: &'a [T],
398}
399
400impl<'a, T> Features<'a, T>
401where
402 T: Serialize,
403{
404 fn new(features: &'a [T]) -> Self {
405 Self { features }
406 }
407}
408
409impl<T> serde::Serialize for Features<'_, T>
410where
411 T: Serialize,
412{
413 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
414 where
415 S: Serializer,
416 {
417 use serde::ser::SerializeSeq;
418 let mut seq = serializer.serialize_seq(None)?;
419 for feature in self.features.iter() {
420 seq.serialize_element(&FeatureWrapper::new(feature))?;
421 }
422 seq.end()
423 }
424}
425
426struct FeatureWrapper<'t, T> {
427 feature: &'t T,
428}
429
430impl<'t, T> FeatureWrapper<'t, T> {
431 fn new(feature: &'t T) -> Self {
432 Self { feature }
433 }
434}
435
436impl<T> Serialize for FeatureWrapper<'_, T>
437where
438 T: Serialize,
439{
440 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
441 where
442 S: Serializer,
443 {
444 let mut json_object: JsonObject = {
445 let value = serde_json::to_value(self.feature).map_err(|e| {
446 S::Error::custom(format!("Feature was not serializable as JSON - {}", e))
447 })?;
448 match value {
449 JsonValue::Object(object) => object,
450 JsonValue::Null => {
451 return Err(S::Error::custom("expected JSON object but found `null`"))
452 }
453 JsonValue::Bool(_) => {
454 return Err(S::Error::custom("expected JSON object but found `bool`"))
455 }
456 JsonValue::Number(_) => {
457 return Err(S::Error::custom("expected JSON object but found `number`"))
458 }
459 JsonValue::String(_) => {
460 return Err(S::Error::custom("expected JSON object but found `string`"))
461 }
462 JsonValue::Array(_) => {
463 return Err(S::Error::custom("expected JSON object but found `array`"))
464 }
465 }
466 };
467
468 if !json_object.contains_key("geometry") {
469 // Currently it's *required* that the struct's geometry field be named `geometry`.
470 //
471 // A likely failure case for users is naming it anything else, e.g. `point: geo::Point`.
472 //
473 // We could just silently blunder on and set `geometry` to None in that case, but
474 // printing a specific error message seems more likely to be helpful.
475 return Err(S::Error::custom("missing `geometry` field"));
476 }
477 let geometry = json_object.remove("geometry");
478
479 use serde::ser::SerializeMap;
480 let mut map = serializer.serialize_map(Some(3))?;
481 map.serialize_entry("type", "Feature")?;
482 map.serialize_entry("geometry", &geometry)?;
483 map.serialize_entry("properties", &json_object)?;
484 map.end()
485 }
486}
487
488#[cfg(test)]
489mod tests {
490 use super::*;
491 use crate::JsonValue;
492
493 use serde_json::json;
494
495 use std::str::FromStr;
496
497 #[test]
498 fn happy_path() {
499 #[derive(Serialize)]
500 struct MyStruct {
501 geometry: crate::Geometry,
502 name: String,
503 }
504
505 let my_feature = {
506 let geometry = crate::Geometry::new(crate::Value::Point(vec![0.0, 1.0]));
507 let name = "burbs".to_string();
508 MyStruct { geometry, name }
509 };
510
511 let expected_output_json = json!({
512 "type": "Feature",
513 "geometry": {
514 "coordinates":[0.0,1.0],
515 "type":"Point"
516 },
517 "properties": {
518 "name": "burbs"
519 }
520 });
521
522 let actual_output = to_feature_string(&my_feature).unwrap();
523 let actual_output_json = JsonValue::from_str(&actual_output).unwrap();
524 assert_eq!(actual_output_json, expected_output_json);
525 }
526
527 mod optional_geometry {
528 use super::*;
529 #[derive(Serialize)]
530 struct MyStruct {
531 geometry: Option<crate::Geometry>,
532 name: String,
533 }
534
535 #[test]
536 fn with_some_geom() {
537 let my_feature = {
538 let geometry = Some(crate::Geometry::new(crate::Value::Point(vec![0.0, 1.0])));
539 let name = "burbs".to_string();
540 MyStruct { geometry, name }
541 };
542
543 let expected_output_json = json!({
544 "type": "Feature",
545 "geometry": {
546 "coordinates":[0.0,1.0],
547 "type":"Point"
548 },
549 "properties": {
550 "name": "burbs"
551 }
552 });
553
554 let actual_output = to_feature_string(&my_feature).unwrap();
555 let actual_output_json = JsonValue::from_str(&actual_output).unwrap();
556 assert_eq!(actual_output_json, expected_output_json);
557 }
558
559 #[test]
560 fn with_none_geom() {
561 let my_feature = {
562 let geometry = None;
563 let name = "burbs".to_string();
564 MyStruct { geometry, name }
565 };
566
567 let expected_output_json = json!({
568 "type": "Feature",
569 "geometry": null,
570 "properties": {
571 "name": "burbs"
572 }
573 });
574
575 let actual_output = to_feature_string(&my_feature).unwrap();
576 let actual_output_json = JsonValue::from_str(&actual_output).unwrap();
577 assert_eq!(actual_output_json, expected_output_json);
578 }
579
580 #[test]
581 fn without_geom_field() {
582 #[derive(Serialize)]
583 struct MyStructWithoutGeom {
584 // geometry: Option<crate::Geometry>,
585 name: String,
586 }
587 let my_feature = {
588 let name = "burbs".to_string();
589 MyStructWithoutGeom { name }
590 };
591
592 let actual_output = to_feature_string(&my_feature).unwrap_err();
593 let error_message = actual_output.to_string();
594
595 // BRITTLE: we'll need to update this test if the error message changes.
596 assert!(error_message.contains("missing"));
597 assert!(error_message.contains("geometry"));
598 }
599
600 #[test]
601 fn serializes_whatever_geometry() {
602 #[derive(Serialize)]
603 struct MyStructWithWeirdGeom {
604 // This isn't a valid geometry representation, but we don't really have a way to "validate" it
605 // so serde will serialize whatever. This test exists just to document current behavior
606 // not that it's exactly desirable.
607 geometry: Vec<u32>,
608 name: String,
609 }
610 let my_feature = {
611 let geometry = vec![1, 2, 3];
612 let name = "burbs".to_string();
613 MyStructWithWeirdGeom { geometry, name }
614 };
615
616 let expected_output_json = json!({
617 "type": "Feature",
618 "geometry": [1, 2, 3],
619 "properties": {
620 "name": "burbs"
621 }
622 });
623
624 let actual_output = to_feature_string(&my_feature).unwrap();
625 let actual_output_json = JsonValue::from_str(&actual_output).unwrap();
626 assert_eq!(actual_output_json, expected_output_json);
627 }
628 }
629
630 #[cfg(feature = "geo-types")]
631 mod geo_types_tests {
632 use super::*;
633 use crate::de::tests::feature_collection;
634 use crate::Geometry;
635
636 #[test]
637 fn serializes_optional_point() {
638 #[derive(serde::Serialize)]
639 struct MyStruct {
640 count: usize,
641 #[serde(
642 skip_serializing_if = "Option::is_none",
643 serialize_with = "serialize_optional_geometry"
644 )]
645 geometry: Option<geo_types::Point<f64>>,
646 }
647
648 let my_struct = MyStruct {
649 count: 0,
650 geometry: Some(geo_types::Point::new(1.2, 0.5)),
651 };
652 let json = json! {{
653 "count": 0,
654 "geometry": {
655 "type": "Point",
656 "coordinates": [1.2, 0.5]
657 },
658 }};
659 assert_eq!(json, serde_json::to_value(my_struct).unwrap());
660
661 let my_struct = MyStruct {
662 count: 1,
663 geometry: None,
664 };
665 let json = json! {{
666 "count": 1,
667 }};
668 assert_eq!(json, serde_json::to_value(my_struct).unwrap());
669 }
670
671 #[test]
672 fn geometry_field_without_helper() {
673 #[derive(Serialize)]
674 struct MyStruct {
675 // If we forget the "serialize_with" helper, bad things happen.
676 // This test documents that:
677 //
678 // #[serde(serialize_with = "serialize_geometry")]
679 geometry: geo_types::Point<f64>,
680 name: String,
681 age: u64,
682 }
683
684 let my_struct = MyStruct {
685 geometry: geo_types::point!(x: 125.6, y: 10.1),
686 name: "Dinagat Islands".to_string(),
687 age: 123,
688 };
689
690 let expected_invalid_output = json!({
691 "type": "Feature",
692 // This isn't a valid geojson-Geometry. This behavior probably isn't desirable, but this
693 // test documents the current behavior of what happens if the users forgets "serialize_geometry"
694 "geometry": { "x": 125.6, "y": 10.1 },
695 "properties": {
696 "name": "Dinagat Islands",
697 "age": 123
698 }
699 });
700
701 // Order might vary, so re-parse to do a semantic comparison of the content.
702 let output_string = to_feature_string(&my_struct).expect("valid serialization");
703 let actual_output = JsonValue::from_str(&output_string).unwrap();
704
705 assert_eq!(actual_output, expected_invalid_output);
706 }
707
708 #[test]
709 fn geometry_field() {
710 #[derive(Serialize)]
711 struct MyStruct {
712 #[serde(serialize_with = "serialize_geometry")]
713 geometry: geo_types::Point<f64>,
714 name: String,
715 age: u64,
716 }
717
718 let my_struct = MyStruct {
719 geometry: geo_types::point!(x: 125.6, y: 10.1),
720 name: "Dinagat Islands".to_string(),
721 age: 123,
722 };
723
724 let expected_output = json!({
725 "type": "Feature",
726 "geometry": {
727 "type": "Point",
728 "coordinates": [125.6, 10.1]
729 },
730 "properties": {
731 "name": "Dinagat Islands",
732 "age": 123
733 }
734 });
735
736 // Order might vary, so re-parse to do a semantic comparison of the content.
737 let output_string = to_feature_string(&my_struct).expect("valid serialization");
738 let actual_output = JsonValue::from_str(&output_string).unwrap();
739
740 assert_eq!(actual_output, expected_output);
741 }
742
743 #[test]
744 fn test_to_feature() {
745 #[derive(Serialize)]
746 struct MyStruct {
747 #[serde(serialize_with = "serialize_geometry")]
748 geometry: geo_types::Point<f64>,
749 name: String,
750 age: u64,
751 }
752
753 let my_struct = MyStruct {
754 geometry: geo_types::point!(x: 125.6, y: 10.1),
755 name: "Dinagat Islands".to_string(),
756 age: 123,
757 };
758
759 let actual = to_feature(&my_struct).unwrap();
760 let expected = Feature {
761 bbox: None,
762 geometry: Some(Geometry::new(crate::Value::Point(vec![125.6, 10.1]))),
763 id: None,
764 properties: Some(
765 json!({
766 "name": "Dinagat Islands",
767 "age": 123
768 })
769 .as_object()
770 .unwrap()
771 .clone(),
772 ),
773 foreign_members: None,
774 };
775
776 assert_eq!(actual, expected)
777 }
778
779 #[test]
780 fn serialize_feature_collection() {
781 #[derive(Serialize)]
782 struct MyStruct {
783 #[serde(serialize_with = "serialize_geometry")]
784 geometry: geo_types::Point<f64>,
785 name: String,
786 age: u64,
787 }
788
789 let my_structs = vec![
790 MyStruct {
791 geometry: geo_types::point!(x: 125.6, y: 10.1),
792 name: "Dinagat Islands".to_string(),
793 age: 123,
794 },
795 MyStruct {
796 geometry: geo_types::point!(x: 2.3, y: 4.5),
797 name: "Neverland".to_string(),
798 age: 456,
799 },
800 ];
801
802 let output_string =
803 to_feature_collection_string(&my_structs).expect("valid serialization");
804
805 // Order might vary, so re-parse to do a semantic comparison of the content.
806 let expected_output = feature_collection();
807 let actual_output = JsonValue::from_str(&output_string).unwrap();
808
809 assert_eq!(actual_output, expected_output);
810 }
811 }
812}