0

I'm having trouble serializing the following struct. I have narrowed it down to, that the problem lies within the variable objects, containing trait structs within a HashMap. I'll try to explain my circumstances:

I have the following main struct, which i'm interested in obtaining data from:

#[derive(Serialize)]
pub struct System { 
    pub objects: HashMap<(u32, u32), Box<dyn CommonObjects>>,
    pub paths: Vec<Path>,
    pub loops: Vec<Loop>,
    pub physics: Physics,

    pub run_limit_loops: u32,
    pub run_limit_paths: u32,
    pub error_tol_loops: f64,
    pub error_tol_paths: f64,

    pub unknown_flow_outlets: u32,
    pub unknown_pumps_id: u32,
    pub system: u32,
}

The kind of structs which are contained within objects are as the following:

pub struct Outlet {

    // Variables found in all structs contained within 'objects'
    pub Q: f64,
    pub hL: f64,
    pub ob: u32,
    pub id: u32,
    pub active: bool,
    pub system: u32,
    pub con_Q: HashMap<u32, f64>,

    // Variables found only in this struct.        
    pub p_dyn: f64,
    pub flow_specified: String,
    pub submerged: bool,
}

pub struct Pipe {
    // Variables found in all structs contained within 'objects'        
    pub Q: f64,
    pub hL: f64,
    pub ob: u32,
    pub id: u32,
    pub active: bool,
    pub system: u32,
    pub con_Q: HashMap<u32, f64>,

    // Variables found only in this struct.    
    pub Re: f64,
    pub f: f64,
}

These struct has some variables which are only used within themselves, and some variables which are common for all structs contained within objects (p_dyn is only used within Outlet, while Q is used for all structs contained within objects) To get these variables, get-function are defined, both within the local struct, and by the trait CommonObject. All i am interested in, is serializing objects in order to get all the variables in a string format, both the common ones, and the ones only appearing locally within a struct, so that i can send the variables to other programs to further visualization.

In this following code the error occurs:

// Where the system struct originates from.
let systems: Vec<System> = system_analyse::analyse_system(data); 

// I try to only serialize the objects contained within one of the system structs.
let jsonstringresult = serde_json::to_string(&systems[0].objects);

match jsonstringresult {
  Ok(v) => {
    println!("{:?}", &v);
    v
  },
  Err(e) => {
    // The error message originates from here.
    println!("An error occured at serializing: {}", e);
    String::new()
  }
}

I get the following error:

An error occured at serializing: key must be a string

I have found this thread discussing the issue of serializing dynamic traits, and i've followed the instructions and added the following to the trait:

pub trait CommonObjects: erased_serde::Serialize {
    ...
}
serialize_trait_object!(CommonObjects);

Which makes the code compile in the first place. I've also found this site getting the same error, but the issue there seems to be with enums. Maybe the problem in this site is related to my problem, but i cant figure out how if so.

I'm open to all sort of feedback and even fundamentally change the structure of the code if so necessary.

Herohtar
  • 5,347
  • 4
  • 31
  • 41
Ólavur Nón
  • 161
  • 1
  • 13
  • 4
    The specific error you're getting isn't about the dynamic objects, it's because the keys of your `HashMap` are tuples (specifically `(u32, u32)`) but JSON can only have string keys (e.g. `String` or `&str`). – Jmb Sep 09 '22 at 20:13
  • Oh my, i’ve completely missed the actual problem, haha! Now i could go continue searching for a solution, but if you have an solution to my problem, then i’ll greatly apprechiate an answer! – Ólavur Nón Sep 09 '22 at 20:28

1 Answers1

1

A quick way to serialize a HashMap with non-string keys to Json is to use the serde_as macro from the serde_with crate.

use serde_with::serde_as;

#[serde_as]
#[derive(Serialize)]
pub struct System { 
    #[serde_as(as = "Vec<(_, _)>")]
    pub objects: HashMap<(u32, u32), Box<dyn CommonObjects>>,
    //...
}

The #[serde_as(as = "Vec<(_, _)>")] encodes the map as a sequence of tuples, representing pairs of keys and values. In Json, this will become an array of 2-element arrays.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • Uuh, looks promising, first thing tomorrow! – Ólavur Nón Sep 09 '22 at 23:48
  • I've tried implementing the additional code you have supplied above, but it still gives the same error; 'key must be a string'. Do you have any thoughs of why this error still would show up? – Ólavur Nón Sep 10 '22 at 01:02
  • Sorry, my bad! The additional code is added to the main struct 'System', and therefore the new "rules" apply only to System, and not 'objects' alone in 'System'. Therefore, serializing 'systems[0].objects' does not work, but serializing 'systems[0]' and 'systems' works, which also is my intention, so thank you alot! – Ólavur Nón Sep 10 '22 at 01:06