charles monod-broca
charles monod-broca

Reputation: 11

Is there a way using serde to define a struct as the result of an "explode"?

I have a serializable struct defined as:

#[derive(Serialize)]
struct Test {
    a: i32,
    b: Vec<i32>,
}

I'd like to store as many records of this struct as there are items in b. And, if possible, doing so recursively and in a generic way (in my real life example, there are way more fields and deeply nested data).

The thing is, I cannot find a way in serde that creates several records from a single one.

Here is my code:

use serde::Serialize;
use serde_query::Deserialize;

#[derive(Serialize)]
struct Test {
    a: i32,
    b: Vec<i32>,
}

#[derive(Serialize, Deserialize)]
struct ExplodedTest {
    #[query("???")]
    a: i32,
    #[query("???")]
    b: i32,
}

fn main() -> Result<(), std::io::Error> {
    let test_struct = Test {
        a: 5,
        b: vec![1, 2, 3, 4],
    };

    let writer = std::io::BufWriter::new(std::fs::File::create("test.json")?);
    let mut serializer = serde_json::Serializer::new(writer);

    test_struct.serialize(&mut serializer)?;

    Ok(())
}

The idea is to turn struct test (that serializes to {"a":5,"b":[1,2,3]}) into several structs, each one serializing to {"a":5,"b":1}, {"a":5,"b":2}, and {"a":5,"b":3}

I don't know a lot about rust, and using serde_query doesn't seem to help a lot.

I was thinking about implementing my own serializer, but not sure if there is an existing solution. Any advice would be very much appreciated!

Upvotes: 1

Views: 49

Answers (1)

Silvestr
Silvestr

Reputation: 809

Frankly speaking, I don't know ready to use a solution too for such a case. I think writing own Serialize implementation should be relatively simple.

#[derive(Debug, Deserialize)]
struct Test {
    a: i32,
    b: Vec<i32>,
}

#[derive(Debug, Serialize, Deserialize)]
struct Output {
    a: i32,
    b: i32,
}

impl Serialize for Test {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut sequence = serializer.serialize_seq(self.b.len().into())?;

        for item in &self.b {
            sequence.serialize_element(&Output {
                a: self.a,
                b: *item,
            })?;
        }

        sequence.end()
    }
}

fn main() {
    let serialized = serde_json::to_string(&Test {
        a: 4,
        b: vec![1, 2, 3],
    })
    .unwrap();

    println!("Serialized: {}", serialized);
}

Then even Deserialize should not be hard to implement too.

struct Data(Vec<Output>);

impl<'de> Deserialize<'de> for Data {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(DataVisitor)
    }
}

struct DataVisitor;

impl<'de> Visitor<'de> for DataVisitor {
    type Value = Data;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a map containing `a` as an integer and `b` as an array of integers")
    }

    fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut a = None;
        let mut b = None;

        while let Some(key) = map.next_key::<String>()? {
            match key.as_str() {
                "a" => {
                    if a.is_some() {
                        return Err(de::Error::duplicate_field("a"));
                    }
                    a = Some(map.next_value()?);
                }
                "b" => {
                    if b.is_some() {
                        return Err(de::Error::duplicate_field("b"));
                    }
                    b = Some(map.next_value()?);
                }
                _ => {
                    return Err(de::Error::unknown_field(&key, &["a", "b"]));
                }
            }
        }

        let a = a.ok_or_else(|| de::Error::missing_field("a"))?;
        let b: Vec<i32> = b.ok_or_else(|| de::Error::missing_field("b"))?;

        Ok(Data(
            b.into_iter().map(|item| Output { a, b: item }).collect(),
        ))
    }
}

fn main() {
    let json_data = r#"{"a":5,"b":[1,2,3]}"#;

    let outputs: Data = serde_json::from_str(json_data).unwrap();

    for output in outputs.0 {
        println!("{:?}", output);
    }
}

You could also add templates for the serialization process.

#[derive(Debug, Serialize, Deserialize)]
struct Output<T: Clone> {
    a: T,
    b: T,
}

#[derive(Debug, Deserialize)]
struct Test<T: Clone> {
    a: T,
    b: Vec<T>,
}

impl<T> Serialize for Test<T>
where
    T: Clone + Serialize,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut sequence = serializer.serialize_seq(self.b.len().into())?;

        for item in &self.b {
            sequence.serialize_element(&Output {
                a: self.a.clone(),
                b: item.clone(),
            })?;
        }

        sequence.end()
    }
}

Also, cloning could be avoided, but it's just more fine-tuning that could be implemented in the future.

Upvotes: 0

Related Questions