Json: Serialising map with integer tuple keys

Created on 26 Jun 2018  路  6Comments  路  Source: serde-rs/json

Hi, I am a novice at using serde_json and trying to solve the following problem.

To serialiize maps, integer keys are converted to string so it is fine (#231). Sadly, integer tuple keys fail:

extern crate serde_json;
use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert((0, 0), 7);
    println!("{}", serde_json::to_string(&map).unwrap());
}

Is there any way to customize how to serialize it?

support

Most helpful comment

Does anyone know what the corresponding deserialize implementation looks like for this?

All 6 comments

What JSON would you want it to produce for this map?

As is:

{ "(0,0)": 7 }

or as a list of key-value pairs:

[ { "key": [0, 0], "val": 7 } ]

The first way would look like this:

extern crate serde;
extern crate serde_json;

use std::collections::HashMap;
use std::fmt::Display;
use std::hash::Hash;

use serde::{Serialize, Serializer};

struct TwoKeyMap<K1, K2, V>(HashMap<(K1, K2), V>);

impl<K1, K2, V> Serialize for TwoKeyMap<K1, K2, V>
where
    K1: Eq + Hash + Display,
    K2: Eq + Hash + Display,
    V: Serialize,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let key = |k1, k2| format!("({},{})", k1, k2);
        serializer.collect_map(self.0.iter().map(|(k, v)| (key(&k.0, &k.1), v)))
    }
}

fn main() {
    let mut map = TwoKeyMap(HashMap::new());
    map.0.insert((0, 0), 7);
    println!("{}", serde_json::to_string(&map).unwrap());
}
{"(0,0)":7}

And the second way:

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

use std::collections::HashMap;
use std::hash::Hash;

use serde::{Serialize, Serializer};

struct ListOfPairsMap<K, V>(HashMap<K, V>);

impl<K, V> Serialize for ListOfPairsMap<K, V>
where
    K: Eq + Hash + Serialize,
    V: Serialize,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        #[derive(Serialize)]
        struct Entry<K, V> {
            key: K,
            val: V,
        }

        serializer.collect_seq(self.0.iter().map(|(key, val)| Entry { key, val }))
    }
}

fn main() {
    let mut map = ListOfPairsMap(HashMap::new());
    map.0.insert((0, 0), 7);
    println!("{}", serde_json::to_string(&map).unwrap());
}
[{"key":[0,0],"val":7}]

Many thanks!
Either way, what I have to do is to define a specific struct and implement serialize for it. Got it.

Does anyone know what the corresponding deserialize implementation looks like for this?

@davefol I ended up implementing Deserialize trait for my struct. First, I _deserialize_ to a _deserializable_ type, then I _transform_ it to the type I want. If you want to go really _Rusty_, you can also implement From or Into for the transformation.

use std::collections::HashMap;

use serde::{Deserialize, Deserializer, Serialize};

#[derive(Serialize, Deserialize, Debug)]
struct KeyVal {
    key: (usize, usize),
    val: usize,
}

#[derive(Debug)]
struct KVMap(HashMap<(usize, usize), usize>);

impl<'de> Deserialize<'de> for KVMap {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Vec::<KeyVal>::deserialize(deserializer)
            .map(|mut v| KVMap(v.drain(..).map(|kv: KeyVal| (kv.key, kv.val)).collect()))
    }
}

fn main() {
    let serialized = r#"[
        {"key":[0,0],"val":1},
        {"key":[1,0],"val":2},
        {"key":[0,1],"val":3},
        {"key":[0,1],"val":4}
    ]"#;
    let deserialized: KVMap = serde_json::from_str(serialized).unwrap();
    println!("{:?}", &deserialized);
}

In most cases, probably you want to implement a custom_deserialize() with the same signature as deserialize() and use it with deserialize_with attribute.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

atenjin picture atenjin  路  3Comments

allan-simon picture allan-simon  路  6Comments

brundonsmith picture brundonsmith  路  3Comments

VictorKoenders picture VictorKoenders  路  6Comments

elliottslaughter picture elliottslaughter  路  5Comments