'rust: adding a field to an existing struct with serde_json
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"}
Solution 1:[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 Value
s and merging them yourself.
Solution 2:[2]
I see two problems with your code:
- You haven't derived
Serialize
forBar
andBar2
. - You're trying to put
a_vec
andb_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
.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | kmdreko |
Solution 2 |