2

I have a pre-defined struct

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct Foo<T> 
where T: Serialize
{
    foo: T
}

struct Bar{
    a: String
}

struct Bar2{
    b: String
}

fn main() -> Result<()>
{
    let a1 = Bar {a: "something".to_owned(),};
    let a2 = Bar {a: "something2".to_owned(),};
    let a_vec: Vec<Bar> = vec![a1, a2];
    let b = Bar2 {b: "something"}
    let b_vec: Vec<Bar2> = vec![b];
    //let foo = Foo {foo: vec![a_vec,b_vec]}

}

How can I put both struct under Foo or is it possible to first serialize Bar to json and add Bar2 as string literals? The result would be a json

{"foo": [{"a": "something"}, {"a": "something2"}], "b": "something"}
dexhunter
  • 578
  • 8
  • 24
  • 1
    It's not really clear what you're asking. The output you want is there, but the constraints of your question are unclear. – cdhowie May 11 '22 at 02:46
  • I agree, the output also seems to be slightly at odds with what you seem to be asking for. The output show has a top level `"foo"` key with the `Bar` value under it and then another top level `"b"` key that seems to be the `Bar2` struct value. Could you clarify what the shape of the output you are looking for is? – Souperman May 11 '22 at 02:50
  • Thanks for commenting, the constraints are 1)no modification of struct 2)output JSON, and I'm also wondering if I can put both `Bar` & `Bar2` under `Foo`? I thought a solution of serializing both structs to `serde_json::Value` first and merge them but I am not sure if there are better solutions regarding to `struct` or `serde`? – dexhunter May 11 '22 at 02:59

2 Answers2

1

You can get this serialized structure by storing both Foo and Bar2 in another struct and merge them together with #[serde(flatten)]. (playground):

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct Foo<T>
where
    T: Serialize,
{
    foo: T,
}

#[derive(Debug, Serialize)]
struct Bar {
    a: String,
}

#[derive(Debug, Serialize)]
struct Bar2 {
    b: String,
}

#[derive(Debug, Serialize)]
struct Outer<T: Serialize> {
    #[serde(flatten)]
    field_1: Foo<T>,
    #[serde(flatten)]
    field_2: Bar2,
}

fn main() {
    let a1 = Bar {
        a: "something".to_owned(),
    };
    let a2 = Bar {
        a: "something2".to_owned(),
    };
    let a_vec: Vec<Bar> = vec![a1, a2];
    let b = Bar2 {
        b: "something".to_owned(),
    };

    let o = Outer {
        field_1: Foo { foo: a_vec },
        field_2: b,
    };

    println!("{}", serde_json::to_string(&o).unwrap());
}
{"foo":[{"a":"something"},{"a":"something2"}],"b":"something"}

If instead by "no modification of struct" you meant by only serializing Foo and just modifying T, then no its not possible to get that output with serde directly. You'd have to do your proposed method by serializing into Values and merging them yourself.

kmdreko
  • 42,554
  • 6
  • 57
  • 106
0

I see two problems with your code:

  • You haven't derived Serialize for Bar and Bar2.
  • You're trying to put a_vec and b_vec into a vector, which you can't do because they're of different type.

The latter problem can be solved by dynamic dispatch. Instead of putting a_vec into the Vec, you put &a_vec as &dyn Serialize, i.e. just give it a reference to some serializable object. Slight trouble is that this won't work with serde::Serialize because it's not object safe. This is what erased_serde is for:

let foo = Foo::<Vec<&dyn erased_serde::Serialize>> {
    foo: vec![&a_vec, &b_vec],
};
println!("{}", serde_json::to_string_pretty(&foo).unwrap());

will work fine. But it will output

{"foo":[[{"a":"something"},{"a":"something2"}],[{"b":"something"}]]}

which is not what you wanted?

To get the output you wanted, i.e. serialize Foo and add a field to it, you could use #[serde(flatten)]:

let foo = Foo { foo: a_vec };
#[derive(Serialize)]
struct Merge<T1: Serialize, T2: Serialize> {
    #[serde(flatten)]
    f1: T1,
    #[serde(flatten)]
    f2: T2,
}
let out = Merge { f1: &foo, f2: &b };
println!("{}", serde_json::to_string_pretty(&out).unwrap());

The output of serializing Merge both the fields from T1 and T2.

Caesar
  • 6,733
  • 4
  • 38
  • 44