I have a data structure Document
, which I would like to serialize other Rust structs to. It is basically a HashMap
for the fields internally, however it interacts with a database API, so I will definitely want to convert other types into those Document
s.
For example this struct
struct Entry {
id: String,
user: String,
duration: u32,
location: (f64, f64),
}
I already have a conversion to the Document
type using the From
trait, however this is an extra place I have to modify when the Entry
struct changes. The implementation uses a DocumentBuilder
and looks like this:
impl From<Entry> for Document {
fn from(entry: Entry) -> Self {
Document::builder()
.name(&entry.id) // set the name of the document
.field("user", entry.user) // add fields ...
.field("duration", entry.duration)
.field("location", entry.location)
.build() // build a Document
}
}
The field
method can assign any value which can be converted to a FieldValue
to a key. So the signature of field
is:
impl DocumentBuilder {
// ...
pub fn field<T: Into<FieldValue>>(mut self, key: &str, value: T) -> Self { ... }
// ...
}
I would like to use serde and its derive feature to automatically serialize the struct and its fields into a Document
. How would I go about doing this? I looked at the wiki for Implementing a Serializer but the example shown writes to a string and I would like to know how I can serialize to a data structure using the builder pattern.
The simplest way to do this would be to use serde_json::from_value
(applicable even if you're not using JSON, but requires all fields to be valid JSON [e.g. no non-string keys in hashmaps]):
let entry = Entry {
a: 24,
b: 42,
c: "nice".to_string()
};
let v = serde_json::to_value(&entry).unwrap();
let document: Document = serde_json::from_value(v).unwrap();
Caveat: the value type of Document
will have to implement Deserialize
, and in a way that can deserialize any value into the correct argument. This can be done by using #[serde(untagged)]
, but may be prone to certain types being wrong, such as u8
being converted to u64
.
Full playground example
A more sophisticated method that doesn't involve any unnecessary copies will require you to write a custom (De)serializer, and a good one to look at would be serde_transcode::transcode
, which does the inverse of the thing you want - it converts between two data formats.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With