3

I have a HashMap as a value in a struct that I'm manually serializing:

pub struct GitInfo { pub branches: HashMap<Oid, Branch>, } 

Branch is something I've defined, but Oid is an external type I don't own, with a to_string() method I'd be happy to use...

I've read How do I use Serde to serialize a HashMap with structs as keys to JSON? but it refers to keys the author defined - I can't implement Serialize for Oid as it's not in my crate. And I can't implement Serialize for HashMap<Oid, Branch> for similar reasons.

Is there some way around this? I could build a wrapper struct around HashMap<Oid, Branch> but that seems like overkill.

It was suggested that I look at How to transform fields during serialization using Serde? or How can I implement Serialize using an existing Display trait implementation? - both seem to boil down to using serialize_with - which I could possibly do, but I'd have to use the derive(Serialize) macro, when I was planning to serialize GitInfo manually (see below). Or I could use a wrapper object.

If those are the only options, I could live with it, but it seems a bit surprising that there isn't a simpler way - is there some way to call a function like the serialize_with macro uses, but from within my GitInfo serialization?

Something like:

impl Serialize for GitInfo { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { let mut state = serializer.serialize_struct("GitInfo", 2)?; state.serialize_field("other bits", &self.other_bits)?; state.serialize_field("branches", /* something */)?; state.end() } } 
2
  • Why are you using serialize_field instead of serialize_map? Commented Oct 9, 2019 at 19:11
  • I'm serializing a struct, GitInfo, with a field which is a HashMap. So I'm using serialize_struct which returns a SerializeStruct which has no serialize_map method. If there is some way to call serialize_map to make a new serialized version of the map, and then insert the results as a parameter to serialize_field, that'd be awesome - but I haven't found a way to do that yet. Commented Oct 9, 2019 at 20:42

1 Answer 1

0

You don't need to build a wrapper struct around HashMap<Oid, Branch> when storing it within your GitInfo struct, but you will need to use a wrapper struct when serializing the HashMap in a custom manner. You can define the new wrapper struct within your Serialize implementation to indicate that it should only be used for serialization.

 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { struct Branches<'a>(&'a HashMap<Oid, Branch>); impl Serialize for Branches<'_> { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { let mut map = serializer.serialize_map(Some(self.0.len()))?; for (oid, branch) in self.0 { map.serialize_entry(&oid.to_string(), &branch)?; } map.end() } } let mut state = serializer.serialize_struct("GitInfo", 2)?; state.serialize_field("branches", &Branches(&self.branches))?; state.serialize_field("other bits", &self.other_bits)?; state.end() } } 

Here is a playground showing a basic implementation of this solution.

Note that the code generated by serialize_with follows this same pattern: it defines a newtype wrapper struct along with a Serialize implementation, and within that trait implementation it calls the function you provide.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.