Skip to content

Commit ea8abb2

Browse files
authored
Merge pull request #158 from cuviper/serde_seq
Add serde_seq to serialize maps as a sequence
2 parents b5ac29e + 3b33a8e commit ea8abb2

File tree

6 files changed

+169
-21
lines changed

6 files changed

+169
-21
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ quickcheck = { version = "0.9", default-features = false }
4848
fnv = "1.0"
4949
lazy_static = "1.3"
5050
fxhash = "0.2.1"
51+
serde_derive = "1.0"
5152

5253
[features]
5354
# Serialization with serde 1.0

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ mod equivalent;
9696
mod mutable_keys;
9797
#[cfg(feature = "serde")]
9898
mod serde;
99+
#[cfg(feature = "serde")]
100+
pub mod serde_seq;
99101
mod util;
100102

101103
pub mod map;

src/serde.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use serde::de::value::{MapDeserializer, SeqDeserializer};
22
use serde::de::{
33
Deserialize, Deserializer, Error, IntoDeserializer, MapAccess, SeqAccess, Visitor,
44
};
5-
use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};
5+
use serde::ser::{Serialize, Serializer};
66

77
use core::fmt::{self, Formatter};
88
use core::hash::{BuildHasher, Hash};
@@ -21,11 +21,7 @@ where
2121
where
2222
T: Serializer,
2323
{
24-
let mut map_serializer = serializer.serialize_map(Some(self.len()))?;
25-
for (key, value) in self {
26-
map_serializer.serialize_entry(key, value)?;
27-
}
28-
map_serializer.end()
24+
serializer.collect_map(self)
2925
}
3026
}
3127

@@ -99,11 +95,7 @@ where
9995
where
10096
Se: Serializer,
10197
{
102-
let mut set_serializer = serializer.serialize_seq(Some(self.len()))?;
103-
for value in self {
104-
set_serializer.serialize_element(value)?;
105-
}
106-
set_serializer.end()
98+
serializer.collect_seq(self)
10799
}
108100
}
109101

src/serde_seq.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//! Functions to serialize and deserialize an `IndexMap` as an ordered sequence.
2+
//!
3+
//! The default `serde` implementation serializes `IndexMap` as a normal map,
4+
//! but there is no guarantee that serialization formats will preserve the order
5+
//! of the key-value pairs. This module serializes `IndexMap` as a sequence of
6+
//! `(key, value)` elements instead, in order.
7+
//!
8+
//! This module may be used in a field attribute for derived implementations:
9+
//!
10+
//! ```
11+
//! # use indexmap::IndexMap;
12+
//! # use serde_derive::{Deserialize, Serialize};
13+
//! #[derive(Deserialize, Serialize)]
14+
//! struct Data {
15+
//! #[serde(with = "indexmap::serde_seq")]
16+
//! map: IndexMap<i32, u64>,
17+
//! // ...
18+
//! }
19+
//! ```
20+
//!
21+
//! Requires crate feature `"serde"` or `"serde-1"`
22+
23+
use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
24+
use serde::ser::{Serialize, Serializer};
25+
26+
use core::fmt::{self, Formatter};
27+
use core::hash::{BuildHasher, Hash};
28+
use core::marker::PhantomData;
29+
30+
use crate::IndexMap;
31+
32+
/// Serializes an `IndexMap` as an ordered sequence.
33+
///
34+
/// This function may be used in a field attribute for deriving `Serialize`:
35+
///
36+
/// ```
37+
/// # use indexmap::IndexMap;
38+
/// # use serde_derive::Serialize;
39+
/// #[derive(Serialize)]
40+
/// struct Data {
41+
/// #[serde(serialize_with = "indexmap::serde_seq::serialize")]
42+
/// map: IndexMap<i32, u64>,
43+
/// // ...
44+
/// }
45+
/// ```
46+
///
47+
/// Requires crate feature `"serde"` or `"serde-1"`
48+
pub fn serialize<K, V, S, T>(map: &IndexMap<K, V, S>, serializer: T) -> Result<T::Ok, T::Error>
49+
where
50+
K: Serialize + Hash + Eq,
51+
V: Serialize,
52+
S: BuildHasher,
53+
T: Serializer,
54+
{
55+
serializer.collect_seq(map)
56+
}
57+
58+
/// Visitor to deserialize a *sequenced* `IndexMap`
59+
struct SeqVisitor<K, V, S>(PhantomData<(K, V, S)>);
60+
61+
impl<'de, K, V, S> Visitor<'de> for SeqVisitor<K, V, S>
62+
where
63+
K: Deserialize<'de> + Eq + Hash,
64+
V: Deserialize<'de>,
65+
S: Default + BuildHasher,
66+
{
67+
type Value = IndexMap<K, V, S>;
68+
69+
fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
70+
write!(formatter, "a sequenced map")
71+
}
72+
73+
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
74+
where
75+
A: SeqAccess<'de>,
76+
{
77+
let capacity = seq.size_hint().unwrap_or(0);
78+
let mut map = IndexMap::with_capacity_and_hasher(capacity, S::default());
79+
80+
while let Some((key, value)) = seq.next_element()? {
81+
map.insert(key, value);
82+
}
83+
84+
Ok(map)
85+
}
86+
}
87+
88+
/// Deserializes an `IndexMap` from an ordered sequence.
89+
///
90+
/// This function may be used in a field attribute for deriving `Deserialize`:
91+
///
92+
/// ```
93+
/// # use indexmap::IndexMap;
94+
/// # use serde_derive::Deserialize;
95+
/// #[derive(Deserialize)]
96+
/// struct Data {
97+
/// #[serde(deserialize_with = "indexmap::serde_seq::deserialize")]
98+
/// map: IndexMap<i32, u64>,
99+
/// // ...
100+
/// }
101+
/// ```
102+
///
103+
/// Requires crate feature `"serde"` or `"serde-1"`
104+
pub fn deserialize<'de, D, K, V, S>(deserializer: D) -> Result<IndexMap<K, V, S>, D::Error>
105+
where
106+
D: Deserializer<'de>,
107+
K: Deserialize<'de> + Eq + Hash,
108+
V: Deserialize<'de>,
109+
S: Default + BuildHasher,
110+
{
111+
deserializer.deserialize_seq(SeqVisitor(PhantomData))
112+
}

test-serde/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ edition = "2018"
1010
[dev-dependencies]
1111
fnv = "1.0"
1212
indexmap = { path = "..", features = ["serde-1"] }
13+
serde = { version = "1.0.99", features = ["derive"] }
1314
serde_test = "1.0.99"

test-serde/src/lib.rs

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
#![cfg(test)]
22

3-
#[macro_use]
4-
extern crate indexmap;
5-
extern crate fnv;
6-
extern crate serde_test;
7-
3+
use fnv::FnvBuildHasher;
4+
use indexmap::{indexmap, indexset, IndexMap, IndexSet};
5+
use serde::{Deserialize, Serialize};
86
use serde_test::{assert_tokens, Token};
97

108
#[test]
11-
fn test_serde() {
9+
fn test_serde_map() {
1210
let map = indexmap! { 1 => 2, 3 => 4 };
1311
assert_tokens(
1412
&map,
@@ -40,8 +38,8 @@ fn test_serde_set() {
4038
}
4139

4240
#[test]
43-
fn test_serde_fnv_hasher() {
44-
let mut map: ::indexmap::IndexMap<i32, i32, ::fnv::FnvBuildHasher> = Default::default();
41+
fn test_serde_map_fnv_hasher() {
42+
let mut map: IndexMap<i32, i32, FnvBuildHasher> = Default::default();
4543
map.insert(1, 2);
4644
map.insert(3, 4);
4745
assert_tokens(
@@ -58,8 +56,8 @@ fn test_serde_fnv_hasher() {
5856
}
5957

6058
#[test]
61-
fn test_serde_map_fnv_hasher() {
62-
let mut set: ::indexmap::IndexSet<i32, ::fnv::FnvBuildHasher> = Default::default();
59+
fn test_serde_set_fnv_hasher() {
60+
let mut set: IndexSet<i32, FnvBuildHasher> = Default::default();
6361
set.extend(1..5);
6462
assert_tokens(
6563
&set,
@@ -73,3 +71,45 @@ fn test_serde_map_fnv_hasher() {
7371
],
7472
);
7573
}
74+
75+
#[test]
76+
fn test_serde_seq_map() {
77+
#[derive(Debug, Deserialize, Serialize)]
78+
#[serde(transparent)]
79+
struct SeqIndexMap {
80+
#[serde(with = "indexmap::serde_seq")]
81+
map: IndexMap<i32, i32>,
82+
}
83+
84+
impl PartialEq for SeqIndexMap {
85+
fn eq(&self, other: &Self) -> bool {
86+
// explicitly compare items in order
87+
self.map.iter().eq(&other.map)
88+
}
89+
}
90+
91+
let map = indexmap! { 1 => 2, 3 => 4, -1 => -2, -3 => -4 };
92+
assert_tokens(
93+
&SeqIndexMap { map },
94+
&[
95+
Token::Seq { len: Some(4) },
96+
Token::Tuple { len: 2 },
97+
Token::I32(1),
98+
Token::I32(2),
99+
Token::TupleEnd,
100+
Token::Tuple { len: 2 },
101+
Token::I32(3),
102+
Token::I32(4),
103+
Token::TupleEnd,
104+
Token::Tuple { len: 2 },
105+
Token::I32(-1),
106+
Token::I32(-2),
107+
Token::TupleEnd,
108+
Token::Tuple { len: 2 },
109+
Token::I32(-3),
110+
Token::I32(-4),
111+
Token::TupleEnd,
112+
Token::SeqEnd,
113+
],
114+
);
115+
}

0 commit comments

Comments
 (0)