-
Notifications
You must be signed in to change notification settings - Fork 559
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deserializing a map with integer keys doesn't work when wrapped in an enum #560
Comments
ping @dtolnay We're hitting this in our codebase to and this makes us to omit using cc @evdokimovs |
This is a duplicate of serde-rs/serde#1183. I'll close so that we keep the discussion in one place. We will be better positioned to fix this after there's been progress on associated type defaults (rust-lang/rust#29661). Without that, this is an extremely complicated issue. |
Thanks @dtolnay! In the meantime, does anyone have a workaround for this issue? I'm experimenting with providing a |
Your deserialize_with function can directly call use serde::{de, Deserialize, Deserializer};
use std::collections::HashMap;
use std::fmt::Display;
use std::hash::Hash;
use std::str::FromStr;
fn de_int_key<'de, D, K, V>(deserializer: D) -> Result<HashMap<K, V>, D::Error>
where
D: Deserializer<'de>,
K: Eq + Hash + FromStr,
K::Err: Display,
V: Deserialize<'de>,
{
let string_map = <HashMap<String, V>>::deserialize(deserializer)?;
let mut map = HashMap::with_capacity(string_map.len());
for (s, v) in string_map {
let k = K::from_str(&s).map_err(de::Error::custom)?;
map.insert(k, v);
}
Ok(map)
} If you need to optimize for performance or memory usage, the following will likely do better by not allocating the string keys or the string to string map. use serde::de::{self, Deserialize, DeserializeSeed, Deserializer, MapAccess, Visitor};
use std::collections::HashMap;
use std::fmt::{self, Display};
use std::hash::Hash;
use std::marker::PhantomData;
use std::str::FromStr;
fn de_int_key<'de, D, K, V>(deserializer: D) -> Result<HashMap<K, V>, D::Error>
where
D: Deserializer<'de>,
K: Eq + Hash + FromStr,
K::Err: Display,
V: Deserialize<'de>,
{
struct KeySeed<K> {
k: PhantomData<K>,
}
impl<'de, K> DeserializeSeed<'de> for KeySeed<K>
where
K: FromStr,
K::Err: Display,
{
type Value = K;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(self)
}
}
impl<'de, K> Visitor<'de> for KeySeed<K>
where
K: FromStr,
K::Err: Display,
{
type Value = K;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string")
}
fn visit_str<E>(self, string: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
K::from_str(string).map_err(de::Error::custom)
}
}
struct MapVisitor<K, V> {
k: PhantomData<K>,
v: PhantomData<V>,
}
impl<'de, K, V> Visitor<'de> for MapVisitor<K, V>
where
K: Eq + Hash + FromStr,
K::Err: Display,
V: Deserialize<'de>,
{
type Value = HashMap<K, V>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a map")
}
fn visit_map<A>(self, mut input: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut map = HashMap::new();
while let Some((k, v)) =
input.next_entry_seed(KeySeed { k: PhantomData }, PhantomData)?
{
map.insert(k, v);
}
Ok(map)
}
}
deserializer.deserialize_map(MapVisitor {
k: PhantomData,
v: PhantomData,
})
} |
Wow, that's super helpful @dtolnay, thanks so much! |
Not sure what's causing this behavior, but I have a minimal reproduction:
Try it out at: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=dbf570c164d60ccf734c375e05b79aa0
The text was updated successfully, but these errors were encountered: