1

Trying to serialize two different Vec fields into a single array in the JSON output. I can't figure out how to implement the serialize() method:

struct Base<'a> {
    workspace: Vec<Workspace<'a>>,
    methods: Vec<Request<'a>>,
    // other fields ...
}

impl Serialize for Base<'_> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let mut state = serializer.serialize_struct("Base", 5)?;

        state.serialize_field("resources", &self.methods)?;
        state.serialize_field("resources", &self.workspace)?;
        // other fields ...

        state.end()
    }
}

I would like to serialize both workspace and methods fields together, however the last "resources" field overwrites the first one. I tried to workaround it using something like this, but it yields an error because serializer moves:

let mut resources = serializer.serialize_seq(Some(self.workspace.len() + self.methods.len()))?;

self.workspace.iter().for_each(|f| { resources.serialize_element(f); });
self.methods.iter().for_each(|f| { resources.serialize_element(f); });

resources.end();

So how would I tie these two together?

kmdreko
  • 42,554
  • 6
  • 57
  • 106
Rushmore75
  • 43
  • 4
  • How would you like your output to look? Say e.g. you have a `Base { workspace: vec![0, 2], methods: vec![1, 3] }`, would you like a `"resources": [0,1,2,3]` or a `"resources": [[0,2], [1,3]]` or a `"resources": [{"workspace": 0, "methods": 1},{"workspace": 2, "methods": 3}]`? – Caesar Dec 01 '22 at 00:36

1 Answers1

2

The easiest method is to transform your data into the Rust/serde equivalent of whatever JSON structure you want in the serialization function:

// in fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
// Declare whatever structs you need to make the required shape
#[derive(Serialize)]
struct WM {
    workspace: Workspace,
    method: Request,
}
state.serialize_field(
    "resources",
    &self
        .methods
        .iter()
        .zip(self.workspace.iter())
        .map(|(&method, &workspace)| WM { workspace, method })
        .collect::<Vec<_>>(),
)?;

Playground

However, this will create an extra allocation. If you're somehow super-keen on speed or memory usage, you could do without

state.serialize_field("resources", &ConcatSerializer(&self.workspace, &self.methods))?;
struct ConcatSerializer<'a>(&'a [Workspace], &'a [Request]);
impl<'a> Serialize for ConcatSerializer<'a> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let mut state = serializer.serialize_seq(Some(self.0.len() + self.1.len()))?;
        for a in self.0 {
            state.serialize_element(a)?;
        }
        for b in self.1 {
            state.serialize_element(b)?;
        }
        state.end()
    }
}

Playground

Caesar
  • 6,733
  • 4
  • 38
  • 44