Serde with HashMap where value uses a remote type

I am trying to serde a HashMap where the value type uses a remote type for serde, but can't get it working.

// --- other_crate: external---
pub enum ExternalType {
    A,
    B,
}

// --- my_crate ---
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(remote = "other_crate::ExternalType")]
enum MyType {
    A,
    B,
}

// want to serialize a HashMap with values ExernalType via MyType.
let mut map = std::collections::HashMap::<String, other_crate::ExternalType>::new();
map.insert("a", other_crate::ExternalType::A);
serde_json::to_string(map);

I've tried playing with the different attributes and implementing a custom de/serializer but can't get them working and am stuck on what I'm doing wrong, so it seemed like a working example would help guide me.

Bonus
It seems that if a type can be de/serialized via a remote type, then data structures containing that type should also be de/serializable. Is there some limitation preventing serde from implementing this via a blanket implementation?

There’s the useful serde_with crate which helps with such things. Here’s how to make your remote = … implementations implement their traits, and then 2 ways how to use it:

/*
[dependencies]
serde = { version = "1.0.195", features = ["derive"] }
serde_json = "1.0.111"
serde_with = "3.4.0"
*/

use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use serde_with::{ser::SerializeAsWrap, serde_as, DeserializeAs, Same, SerializeAs};

mod other_crate {
    pub enum ExternalType {
        A,
        B,
    }
}

// --- my_crate ---
#[derive(Serialize, Deserialize)]
#[serde(remote = "other_crate::ExternalType")]
enum MyType {
    A,
    B,
}
impl SerializeAs<other_crate::ExternalType> for MyType {
    fn serialize_as<S>(source: &other_crate::ExternalType, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        MyType::serialize(source, serializer)
    }
}
impl<'de> DeserializeAs<'de, other_crate::ExternalType> for MyType {
    fn deserialize_as<D>(deserializer: D) -> Result<other_crate::ExternalType, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        MyType::deserialize(deserializer)
    }
}

fn main1() {
    // want to serialize a HashMap with values ExernalType via MyType.
    let mut map = HashMap::<String, other_crate::ExternalType>::new();
    map.insert("a".to_owned(), other_crate::ExternalType::A);
    dbg!(serde_json::to_string::<SerializeAsWrap<'_, _, HashMap<Same, MyType>>>(&(&map).into()).unwrap());
}

#[serde_as]
#[derive(Serialize)]
#[serde(transparent)]
struct WrappedMap<'a, T: Serialize>(
    #[serde_as(as = "&HashMap<_, MyType>")] &'a HashMap<T, other_crate::ExternalType>,
);

fn main2() {
    // want to serialize a HashMap with values ExernalType via MyType.
    let mut map = HashMap::<String, other_crate::ExternalType>::new();
    map.insert("a".to_owned(), other_crate::ExternalType::A);
    dbg!(serde_json::to_string(&WrappedMap(&map)).unwrap());
}

(run online on rustexplorer.com)

1 Like

Thanks so much for the guidance! I played around for a bit, and think my example didn't fully express the problem.

I am trying to use the HashMap as the value to a tuple-like enum variant, where the enum should be de/serializable.

#[derive(serde::Serialize, serde::deserialize)]
enum MyEnum {
    VarExternalType(HashMap<String, other_crate::ExternalType>),
}

Unfortunately, the serde_with::serde_as trait can not be used on enum variants. What is the best workaround to this? Just use a wrapper type? Implement Serialize and Deserialize, delegating values to MyType's functionality?

You use it on the field, not the variant.


#[serde_as]
#[derive(Serialize, Deserialize)]
enum MyEnum {
    VarExternalType(
        #[serde_as(as = "HashMap<_, MyType>")] HashMap<String, other_crate::ExternalType>,
    ),
}

fn main() {
    let mut map = HashMap::<String, other_crate::ExternalType>::new();
    map.insert("a".to_owned(), other_crate::ExternalType::A);
    let json = serde_json::to_string(&MyEnum::VarExternalType(map)).unwrap();
    println!("{json}");
}

(run online on rustexplorer.com)

{"VarExternalType":{"a":"A"}}
2 Likes

Ahh.. of course!

Thank you!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.