Search code examples
serializationrustdeserializationtoml

Can you serialize a vector of stuct's to TOML in rust?


Summary

I'm writing a program in rust and I would prefer use a TOML file to store a vector of struct's. However I can't figure out how to store a vector of struct's in a TOML file. I am able to do this using JSON but was hoping for TOML so just confirming that I'm not overlooking something (not even sure if the TOML format would be able to support what I want). Therefore, I'm trying to find out if anyone knows of a way use rust to serialize a vector of struct's to TOML and more importantly to deserialize it back into a vector.

Error message (on attempt to deserialize)

thread 'main' panicked at 'called Result::unwrap() on an Err value: Error { inner: ErrorInner { kind: Wanted { expected: "a table key", found: "a right bracket" }, line: Some(0), col: 2, at: Some(2), message: "", key: [] } }', src/main.rs:22:55 note: run with RUST_BACKTRACE=1 environment variable to display a backtrace

Excerpt from Cargo.toml

[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1.0.86"
toml = "0.5.9"

Example code

Link to code on Playground

use serde::{Deserialize, Serialize};

#[derive(PartialEq, Debug, Serialize, Deserialize)]
struct Point {
    x: i32,
}

/// An ordered list of points (This is what I want to store in the TOML file)
type Points = Vec<Point>;

fn main(){
    // Create sample data to test on
    let original: Points = vec![Point { x: 1 }];

    // Working code that converts it to json and back
    let json = serde_json::to_string(&original).unwrap();
    let reconstructed: Points = serde_json::from_str(&json).unwrap();
    assert_eq!(original, reconstructed); // No panic

    // "Desired" code that converts it to toml but unable to deserialize
    let toml = toml::to_string(&original).unwrap();
    let reconstructed: Points = toml::from_str(&toml).unwrap(); // Panics!
    assert_eq!(original, reconstructed); 
}

Output of toml::to_string(&original).unwrap()

[[]]
x = 1

Explanation of example code

In the example code I create some sample data then convert it to JSON and back with no issue. I then try to convert it to TOML, which doesn't give an error but the output doesn't make sense to me. Then I try to convert it back into a rust vector and that triggers the error. My biggest problem is I'm not even sure how I would expect the TOML file to look for a valid representation of a vector with multiple struct's.

Related Questions / Research

I wasn't able to find any information for creating a vector with multiple struct's the closest I could find is this question, and while the question looks like it should solve my problem the actual problem was serializing enums and the solution hence refers to that and doesn't solve my problem.


Solution

  • It seems that to represent an array of tables in Toml the syntax is

    [[points]]
    x = 1
    [[points]]
    x = 2
    

    So backtracking from Toml syntax and original panic error Error { inner: ErrorInner { kind: Wanted { expected: "a table key", found: "a right bracket" }: Introducing a wrapper struct to represent table key fixes the issue.

    use serde::{Deserialize, Serialize};
    
    #[derive(PartialEq, Debug, Serialize, Deserialize)]
    struct Point {
        x: i32,
    }
    
    #[derive(PartialEq, Debug,Serialize, Deserialize)]
    struct Points {
        points: Vec<Point>
    }
    
    impl From<Vec<Point>> for Points {
        fn from(points: Vec<Point>) -> Self {
            Points {
                points
            }
        }
    }
    
    fn main(){
        let original: Points = vec![Point { x: 1 }, Point {x : 2}].into();
    
        let json = serde_json::to_string(&original).unwrap();
        let reconstructed: Points = serde_json::from_str(&json).unwrap();
        assert_eq!(original, reconstructed);
    
        let toml = toml::to_string(&original).unwrap();
        let reconstructed: Points = toml::from_str(&toml).unwrap();
    
        assert_eq!(original, reconstructed);
    }