indexmap-rs / indexmap

A hash table with consistent order and fast iteration; access items by key or sequence index
https://docs.rs/indexmap/
Other
1.71k stars 150 forks source link

Ordered Serialization #279

Closed diogoaurelio closed 12 months ago

diogoaurelio commented 1 year ago

Hi, rust noob here (couple of hours under my belt), trying to serialize a map with the same order as insertion, but unable to do so:

error[E0432]: unresolved import `indexmap::map::serde_seq`
  --> src/main.rs:3:5
   |
3  | use indexmap::map::serde_seq;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^ no `serde_seq` in `map`
   |
note: found an item that was configured out
  --> /home/makina/.cargo/registry/src/index.crates.io-6f17d22bba15001f/indexmap-2.0.2/src/map.rs:10:9
   |
10 | pub mod serde_seq;
   |         ^^^^^^^^^
   = note: the item is gated behind the `serde` feature

Here the sample code:

   use indexmap::IndexMap;
   use indexmap::map::serde_seq;

#[derive(Serialize)]
struct MyStruct {
    #[serde(with = "indexmap::map::serde_seq")]
    data: IndexMap<String, String>,
}

    let mut map = IndexMap::new();

    map.insert(String::from("banana"), String::from("1"));
    map.insert(String::from("apple"), String::from("3"));
    let my_struct = MyStruct { data: map };
    let serialized = serde_json::to_string(&my_struct).expect("Serialization failed");
    println!("{}", serialized);

My cargo:

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
indexmap = { version = "2.0.2", feature = ["serde"] }

From this documentation, I understood that I should be able to serialize with order.

Many thanks in advance.

cuviper commented 1 year ago

Your example works for me, after adding use serde::Serialize; and wrapping the code starting at let in fn main(). Are you perhaps using multiple workspaces where the features are not the same in all instances?

Also, you don't need use indexmap::map::serde_seq; if you're also writing that full path in the serde(with = ...), so you could either remove that use or change to serde(with = "serde_seq"). (But that's just an "unused" warning as-is.)

The serde_seq mode will serialize as a list of key-value pairs, like:

{"data":[["banana","1"],["apple","3"]]}

If you want it to look like a JSON object with order, there's a serde_json feature "preserve_order" for that, which even uses IndexMap itself! Then without the serde(with) annotation, the default serialization output in-order looks like:

{"data":{"banana":"1","apple":"3"}}
cuviper commented 1 year ago

Actually, I think that "preserve_order" feature only matters for serde_json::Map as found in its generic Value representation. When you serialize directly to_string, it seems to preserve the order regardless.

cuviper commented 12 months ago

Feel free to reopen if you have further questions!