3

I'm trying to deserialize an array of arrays (represents a table of string cells) into a custom struct in Rust with serde_json. I know that using serde_json::Value is enough for this simple case but I would like to construct a custom type.

use serde::{Deserialize};
use serde_json::{self, Result};

#[derive(Deserialize, Debug)]
pub struct Row {
    pub cells: Vec<String>,
}

#[derive(Deserialize, Debug)]
pub struct Table {
    pub rows: Vec<Row>,
}

impl Table {

    pub fn new(data: &str) -> Result<Table> {
        let table = serde_json::from_str(data);
        table
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn from_json_string() {
        let test_table = r#"
[
  ["0,1", "0,2", "0,3"], 
  ["1,1", "1,2", "1,3"]
]
"#;
        let table: Table = Table::new(&test_table).unwrap();
        assert_eq!(table.rows.len(), 2);
    }

}

With this code, the test panics with Error("invalid type: string \"0,1\", expected struct Row".

How should I define the structs for this simple JSON string?

Natxo.Piq
  • 105
  • 2
  • 9

2 Answers2

4

You want to add the tag #[serde(transparent)] to the structs

use serde::{Deserialize};
use serde_json::{self, Result};

#[derive(Deserialize, Debug)]
#[serde(transparent)] 
pub struct Row {
    pub cells: Vec<String>,
}

#[derive(Deserialize, Debug)]
#[serde(transparent)] 
pub struct Table {
    pub rows: Vec<Row>,
}

impl Table {

    pub fn new(data: &str) -> Result<Table> {
        let table = serde_json::from_str(data);
        table
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn from_json_string() {
        let test_table = r#"
[
  ["0,1", "0,2", "0,3"], 
  ["1,1", "1,2", "1,3"]
]
"#;
        let table: Table = Table::new(&test_table).unwrap();
        assert_eq!(table.rows.len(), 2);
    }

}

Playground

#[serde(transparent)]

Serialize and deserialize a newtype struct or a braced struct with one field exactly the same as if its one field were serialized and deserialized by itself. Analogous to #[repr(transparent)].

Taken from the attributes page here: https://serde.rs/container-attrs.html

Community
  • 1
  • 1
8176135
  • 3,755
  • 3
  • 19
  • 42
  • Yes! But I must admit that the explanation is not very 'transparent' for me. I will need to invest more time in reading serde documentation. Thanks!!! – Natxo.Piq Jan 23 '20 at 08:09
-1

Your input is not a valid JSON and since you are using serde_json it would be wise for the input to actually be a JSON.

You can change your code to something similar to code below:

#[test]
fn from_json_string() {
    let test_table = r#"
    {
        "rows" : [
            {
                "cells" : ["1", "2"]
            },
            {
                "cells" : ["3", "4"]
            }
        ]
    }"#;

    let table: Table = Table::new(&test_table).unwrap();
    assert_eq!(table.rows.len(), 2);
}
GrayCat
  • 1,749
  • 3
  • 19
  • 30
  • 1
    The input is a very valid JSON string! problem is that serde expects to find a JSON object to be deserialized into a struct and in this case I want a JSON *array* to be deserialized into the structs I created. Then I need to make the struct 'transparent' to deserialize into the inner Vector field. At least that's what I understood. – Natxo.Piq Jan 23 '20 at 08:45