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?
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.
Most helpful comment
Does anyone know what the corresponding deserialize implementation looks like for this?