Reputation: 11
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
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