Search code examples
jsonrustserde

How to deserialize a map of string keys and numbers or string values as a HashMap<String, String> with serde?


I have a single level json where the values are of i32s, f32s, and Strings. How to deserialize that json to HashMap<String, String>?

{
    "key1": "value1",
    "key2": 0,
    "key3": 6.7
}

How to deserialize it? Is there a way in serde that can help here?

let json: HashMap<String, String>= serde_json::from_str(r#"{"key1": "value1", "key2": 0, "key3": 6.7}"#).unwrap();

Edit

I'm wondering if there is a way provided by serde like a flag to avoid the overhead due to the additional iteration.


Solution

  • You don't specify what the Rust strings should contain. This solution will keep the value part exactly like they are in JSON, including trailing digits and quotes around the string. This is the result of running this code:

    [src/main.rs:15] map = {
        "key1": "\"value1\"",
        "key2": "0",
        "key3": "6.7",
        "key4": "0.000",
    }
    
    use serde::de::*;
    use std::collections::HashMap;
    
    fn main() {
        let j = r#"
        {
            "key1": "value1",
            "key2": 0,
            "key3": 6.7,
            "key4": 0.000
        }"#;
    
        let mut deser = serde_json::Deserializer::from_str(j);
        let map = deser_hashmap(&mut deser).unwrap();
        dbg!(map);
    }
    
    fn deser_hashmap<'de, D>(deserializer: D) -> Result<HashMap<String, String>, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct MapVisitor;
    
        impl<'de> Visitor<'de> for MapVisitor {
            type Value = HashMap<String, String>;
    
            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                formatter.write_str("a map")
            }
    
            fn visit_map<A>(self, mut access: A) -> Result<Self::Value, A::Error>
            where
                A: MapAccess<'de>,
            {
                let mut values = HashMap::new();
    
                // We use RawValue here to access the JSON value exactly as it is occuring in the input.
                while let Some((key, value)) = (access.next_entry())?
                    .map(|(k, v): (String, &'de serde_json::value::RawValue)| (k, v.get().to_owned()))
                {
                    values.insert(key, value);
                }
    
                Ok(values)
            }
        }
    
        let visitor = MapVisitor;
        deserializer.deserialize_map(visitor)
    }
    

    Code on Playground