Reputation: 3216
From a struct describing a puzzle grid, I'd like to be able to serialize a nested array:
struct Grid {
display: String,
solution: String,
width: usize,
height: usize,
}
Assuming a struct of Grid { display: "????", solution: "1234", width: 2, height: 2 }
, I'd like the output to look like the following (in JSON):
[
[
{
"display": "?",
"solution": "1"
},
{
"display": "?",
"solution": "2"
}
],
[
{
"display": "?",
"solution": "3"
},
{
"display": "?",
"solution": "4"
}
]
]
My initial draft implementation looks like this:
impl<'a> Serialize for Grid<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut columns = serializer.serialize_seq(Some(self.height))?;
for column_index in 0..self.height {
let mut column = columns.serialize_seq(Some(self.width))?;
for row_index in 0..self.width {
let mut cell = column.serialize_map(Some(2))?;
let cell_index = column_index * self.height + row_index;
cell.serialize_entry("display", self.display.chars().nth(cell_index).unwrap())?;
cell.serialize_entry("solution", self.solution.chars().nth(cell_index).unwrap())?;
cell.end()?;
}
column.end()?;
}
columns.end()
}
}
However, SerializeSeq
does not expose another serialize_seq
method for further nesting. How can I serialize a nested array out of one struct like this?
Upvotes: 3
Views: 3590
Reputation: 114230
Serde has nifty from
and into
annotations that you can use to specify an alternate type to use for serialization. As long as Grid
implements Clone
(which should be trivial and harmless), and you implement the appropriate conversions, you are good to go. The nice thing about this approach is that you can serialize Grid
exactly as you want directly, with a minimum of custom types:
#[derive(Clone, Deserialize, Serialize)]
#[serde(from="Vec<Vec<Cell>>", into="Vec<Vec<Cell>>"]
struct Grid {
cell: Cell,
width: usize,
height: usize,
}
#[derive(Clone, Deserialize, Serialize)]
struct Cell {
display: String,
solution: String,
}
impl From<Vec<Vec<Cell>>> for Grid {
fn from(value: Vec<Vec<Cell>>) -> Self {
// Optionally check that all the values are identical here
Self {
cell: value[0][0],
width: value[0].len(),
height: value.len(),
}
}
}
impl Into<Vec<Vec<Cell>>> for Grid {
fn into(self) -> Vec<Vec<Grid>> {
let row = vec![self.cell; self.width];
vec![row; self.height]
}
}
If you run into issues with traits on externally defined objects, you can always make a custom type for serialization. That shouldn't happen with the code above, but I prefer to implement From
rather than into most of the time, which you can't do for Vec
, since you own neither Vec
nor From
.
That being said, you can't get away from both redundant data structures and custom serializers. You need to specify a way to map your Grid
to JSON: you can choose to turn it into a common structure that Serde understands, or provide the same information via custom instructions, but you can't escape it altogether.
Upvotes: 1
Reputation: 1269
A helper struct Cell with display and solution as fields will make the serialization easier. Then, you can construct a list of Cells in a functional way with the iterator adapters including zip and map from the solution and display values of the grid. Then, use the chunks adapter to convert the one-dimension vector into a two-dimension vector by the row size. At last, use the macro json! to generate the json string.
use serde::{Deserialize, Serialize};
use serde_json::json;
struct Grid {
display: String,
solution: String,
width: usize,
height: usize,
}
#[derive(Serialize, Deserialize)]
struct Cell {
display: char,
solution: char,
}
fn main() {
let grid = Grid { display: "????".to_string(), solution: "1234".to_string(), width: 2, height: 2 };
let cells :Vec<Cell> = grid.display.chars().zip(grid.solution.chars()).map(|(a, b)| Cell {display: a, solution: b} ).collect();
let rows : Vec<&[Cell]>= cells.chunks(grid.width).collect();
print!("{:#}", json!(rows));
}
Upvotes: 1
Reputation: 23244
You need a couple of helper structs to represent a row and a cell:
use serde::*; // 1.0.130
use serde::ser::*;
struct Grid {
display: String,
solution: String,
width: usize,
height: usize,
}
impl Serialize for Grid {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut rows = serializer.serialize_seq (Some (self.height))?;
for idx in 0..self.height {
rows.serialize_element (&Row { grid: self, start_idx: idx * self.width })?;
}
rows.end()
}
}
struct Row<'a> {
grid: &'a Grid,
start_idx: usize,
}
impl<'a> Serialize for Row<'a> {
fn serialize<S: Serializer> (&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut row = serializer.serialize_seq (Some (self.grid.width))?;
for idx in 0..self.grid.width {
row.serialize_element (&Cell { grid: self.grid, idx: self.start_idx + idx })?;
}
row.end()
}
}
struct Cell<'a> {
grid: &'a Grid,
idx: usize,
}
impl<'a> Serialize for Cell<'a> {
fn serialize<S: Serializer> (&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut cell = serializer.serialize_map (Some (2))?;
cell.serialize_entry ("display", &self.grid.display.chars().nth (self.idx).unwrap())?;
cell.serialize_entry ("solution", &self.grid.solution.chars().nth (self.idx).unwrap())?;
cell.end()
}
}
Upvotes: 2