serde-map-to-array   ![cibadge] ![cratesiobadge] ![docsbadge] ![msrv_badge]


This crate provides unofficial serde helpers to support converting a map to a sequence of named key-value pairs for human-readable encoding formats.

This allows for a stable schema in the face of a mutable map.

For example, let's say we have a map containing the values [(1, "one"), (2, "two"), (3, "three")]. Encoded to JSON this is:

json {"1":"one","2":"two","3":"three"}

We cannot specify a schema for this JSON Object though unless the contents of the map are guaranteed to always contain two entries under the keys 1, 2 and 3.

This crate allows for such a map to be encoded to a JSON Array of Objects, each containing exactly two elements with static names:

json [{"key":1,"value":"one"},{"key":2,"value":"two"},{"key":3,"value":"three"}]

for which a schema can be generated.

Furthermore, this avoids encoding the key type of the map to a string.

By default, the key-value pairs will be given the labels "key" and "value", but this can be modified by providing your own labels via a struct which implements KeyValueLabels.

Note that for binary (non-human-readable) encoding formats, default serialization and deserialization is retained.

no_std

By default, the crate is no_std, but uses alloc. In this case, support for BTreeMaps and BTreeMap-like types is provided.

If feature std is enabled then support for HashMaps and HashMap-like types is also provided, but no_std support is disabled.

Examples

Using the default field values "key" and "value"

```rust use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; use serdemapto_array::BTreeMapToArray;

[derive(Default, Serialize, Deserialize)]

struct Data { #[serde(with = "BTreeMapToArray::")] inner: BTreeMap, }

let mut data = Data::default(); data.inner.insert(1, "one".tostring()); data.inner.insert(2, "two".tostring());

asserteq!( serdejson::to_string(&data).unwrap(), r#"{"inner":[{"key":1,"value":"one"},{"key":2,"value":"two"}]}"# ); ```

Using non-default field labels

```rust use std::collections::HashMap; use serde::{Deserialize, Serialize}; use serdemapto_array::{KeyValueLabels, HashMapToArray};

struct MyKeyValueLabels;

impl KeyValueLabels for MyKeyValueLabels { const KEY: &'static str = "id"; const VALUE: &'static str = "name"; }

[derive(Default, Serialize, Deserialize)]

struct Data { #[serde(with = "HashMapToArray::")] inner: HashMap, }

let mut data = Data::default(); data.inner.insert(1, "one".tostring()); data.inner.insert(2, "two".tostring()); // The hashmap orders the entries randomly. let expected_json = if *data.inner.keys().next().unwrap() == 1 { r#"{"inner":[{"id":1,"name":"one"},{"id":2,"name":"two"}]}"# } else { r#"{"inner":[{"id":2,"name":"two"},{"id":1,"name":"one"}]}"# };

asserteq!(serdejson::tostring(&data).unwrap(), expectedjson); ```

Using a custom BTreeMap-like type

```rust use std::collections::{btreemap, BTreeMap}; use serde::{Deserialize, Serialize}; use serdemaptoarray::{BTreeMapToArray, DefaultLabels};

[derive(Serialize, Deserialize)]

struct MyMap(BTreeMap);

/// We need to implement IntoIterator to allow serialization. impl<'a> IntoIterator for &'a MyMap { type Item = (&'a u64, &'a String); type IntoIter = btreemap::Iter<'a, u64, String>; fn intoiter(self) -> Self::IntoIter { self.0.iter() } }

/// We need to implement From<BTreeMap> to allow deserialization. impl From> for MyMap { fn from(map: BTreeMap) -> Self { MyMap(map) } }

[derive(Serialize, Deserialize)]

struct Data { #[serde(with = "BTreeMapToArray::")] inner: MyMap, } ```

Using a HashMap with a non-standard hasher

```rust use std::collections::HashMap; use serde::{Deserialize, Serialize}; use hashhasher::HashBuildHasher; use serdemaptoarray::{DefaultLabels, HashMapToArray};

[derive(Serialize, Deserialize)]

struct Data { #[serde(with = "HashMapToArray::")] inner: HashMap, } ```

Using a custom HashMap-like type

```rust use std::collections::{hashmap::{self, RandomState}, HashMap}; use serde::{Deserialize, Serialize}; use serdemaptoarray::{DefaultLabels, HashMapToArray};

[derive(Serialize, Deserialize)]

struct MyMap(HashMap);

/// We need to implement IntoIterator to allow serialization. impl<'a> IntoIterator for &'a MyMap { type Item = (&'a u64, &'a String); type IntoIter = hashmap::Iter<'a, u64, String>; fn intoiter(self) -> Self::IntoIter { self.0.iter() } }

/// We need to implement From<HashMap> to allow deserialization. impl From> for MyMap { fn from(map: HashMap) -> Self { MyMap(map) } }

[derive(Serialize, Deserialize)]

struct Data { #[serde(with = "HashMapToArray::")] inner: MyMap, } ```

License

serde-map-to-array is distributed under the terms of both the MIT license and the Apache License (Version 2.0).

See LICENSE-MIT and LICENSE-APACHE for details.