Serde documentation says:
All of these can be serialized using Serde out of the box.
serde_json is just for the example, not required in general.
This is exactly what I need, a basic serialization of a struct into some basic binary format. I don't need JSON format, I want to keep it simple to be able to store struct in a file or send it to the network. The documentation is not clear on how to use serde for basic (binary or default) serialization, it only shows example with a JSON but this is not what I am looking for. I also don't want to implement my own serialize method, I want to use the default methods that Serde provides.
This is my example, so how do I make it work?
use serde::{Serialize, Deserialize,Serializer};
#[derive(Serialize, Deserialize, Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 1, y: 2 };
//let serialized = serde::serialize(&point).unwrap(); // <-- doesnt work!
//let serialized = Serializer::serialize(&point); // <-- doesnt work!
//let serialized = point.serialize(Serializer); // <-- doesn't work!
println!("data = {:?}", serialized);
}
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b57a77399280f19664bb004201319b32
This is my dependency line:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
As noted in the comments, there's no "default" format provided.
You have to pick the binary format you want and include it as an additional dependency, just as you would with serde_json.
There's a list of formats at https://serde.rs/#data-formats. Of those serde_json is the only package hosted under https://github.com/serde-rs, all the binary formats are "third-party". Cross-referencing it with the list of crates tagged "serde" sorted by recent downloads, the CBOR crate seems popular.
Related
I am working in a relatively large codebase where options are represented in JSON as arrays, so None is represented in JSON as [] and Some(thing) as [thing]. (Yes, the codebase also contains Haskell, in case you are wondering.) How can I override the default serde_json behaviour, which is to omit optional fields, to match this?
E.g. a struct:
SomeData {
foo: Some(1),
bar: None
}
should be serialized to JSON as:
{
"foo": [1],
"bar": []
}
Of course, one could theoretically implement custom serialization for each and every optional field in every struct that interacts with the codebase but that would be a huge undertaking, even if it were possible.
There don't seem to be any options in the serde_json serialisation of some and none so I imagine that the solution will be creating a new serializer that inherits almost everything from serde_json apart from the Option serialization and deserialization. Are there any examples of projects that do this? It would also be possible to make a fork but maintaining a fork is never much fun.
Of course, one could theoretically implement custom serialization for each and every optional field in every struct that interacts with the codebase
A custom implementation for each and every field is not necessary. By using serialize_with, you only need one transformation function describing the serialization of any serializable Option<T> as a sequence.
fn serialize_option_as_array<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
where
T: Serialize,
S: Serializer,
{
let len = if value.is_some() { 1 } else { 0 };
let mut seq = serializer.serialize_seq(Some(len))?;
for element in value {
seq.serialize_element(element)?;
}
seq.end()
}
Using it in your struct:
use serde_derive::Serialize;
use serde::ser::{Serialize, Serializer, SerializeSeq};
use serde_json;
#[derive(Debug, Serialize)]
struct SomeData {
#[serde(serialize_with = "serialize_option_as_array")]
foo: Option<i32>,
#[serde(serialize_with = "serialize_option_as_array")]
bar: Option<u32>,
}
let data = SomeData {
foo: Some(5),
bar: None,
};
println!("{}", serde_json::to_string(&data)?);
The output:
{"foo":[5],"bar":[]}
Playground
See also:
How to transform fields during serialization using Serde?
My goal is to (de)serialize objects with RFC-3339 timestamps from Json to Rust structs (and vice versa) using serde and time-rs.
I would expect this ...
use serde::Deserialize;
use time::{OffsetDateTime};
#[derive(Deserialize)]
pub struct DtoTest {
pub timestamp: OffsetDateTime,
}
fn main() {
let deserialization_result = serde_json::from_str::<DtoTest>("{\"timestamp\": \"2022-07-08T09:10:11Z\"}");
let dto = deserialization_result.expect("This should not panic");
println!("{}", dto.timestamp);
}
... to create the struct and display the timestamp as the output, but I get ...
thread 'main' panicked at 'This should not panic: Error("invalid type: string \"2022-07-08T09:10:11Z\", expected an `OffsetDateTime`", line: 1, column: 36)', src/main.rs:12:38
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
My dependencies look like this:
[dependencies]
serde = { version = "1.0.138", features = ["derive"] }
serde_json = "1.0.82"
time = { version = "0.3.11", features = ["serde"] }
According to the documentation of the time-rs crate, this seems to be possible but I must be missing something.
The default serialization format for time is some internal format. If you want other formats, you should enable the serde-well-known feature and use the serde module to choose the format you want:
#[derive(Deserialize)]
pub struct DtoTest {
#[serde(with = "time::serde::rfc3339")]
pub timestamp: OffsetDateTime,
}
The solution below is based on the serde_with crate. As per its documentation, it aims to be more flexible and composable.
use serde_with::serde_as;
use time::OffsetDateTime;
use time::format_description::well_known::Rfc3339;
#[serde_as]
#[derive(Deserialize)]
pub struct DtoTest {
#[serde_as(as = "Rfc3339")]
pub timestamp: OffsetDateTime,
}
And the Cargo.toml file should have:
[dependencies]
serde_with = { version = "2", features = ["time_0_3"] }
At the following page are listed all De/Serialize transformations available.
I have a struct that contains a date and I use it with sqlx to retrieve data from my database. So something like:
use sqlx::types::chrono::{DateTime, Utc};
pub struct Account {
pub id: i32,
pub primary_email_id: i32,
pub created: DateTime<Utc>,
}
and
sqlx::query_as!(Account, "select * ...")
This works fine so far. But I also want Account to be serializable via serde. The obvious approach is:
#[derive(Serialize)]
pub struct Account {
...
This fails, because the Serialize trait is not implemented for DateTime<Utc>. I tried the same with PrimitiveDateTime from the time crate with the same result. In theory both should support serde as a feature.
I tried to explicitly add time or chrono as dependency, to enable serde as feature and use the type without the sqlx::types prefix. But in that case it fails because some sqlx traits are not implemented.
I assume that I somehow have to enable the serde feature for the classes brought in by sqlx, but I have no idea how to specify a feature for a feature!?
How to I tell sqlx to enable serde for the time/chrono types?
This depends on the time and chrono Cargo features of sqlx. So ensure the [dependencies] section of your Cargo.toml file includes these. As an example:
sqlx = { version = "0.6.0", features = [ "postgres", "runtime-tokio-native-tls", "offline", "time", "chrono" ] }
(This is essentially the comment of #Stargateur above. All credit to them.)
I need a map with the Option values in my configuration. However, serde seems to ignore any pairs with the None value
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use toml;
#[derive(Debug, Serialize, Deserialize)]
struct Config {
values: HashMap<String, Option<u32>>,
}
fn main() {
let values = [("foo", Some(5)), ("bar", None)]
.iter()
.map(|(name, s)| (name.to_string(), s.clone()))
.collect();
let config = Config { values };
let s = toml::ser::to_string(&config).unwrap();
println!("{}", s);
}
produces
[values]
foo = 5
The same goes for deserializing: I simply cannot represent bar: None in any form,
since the TOML has no notion of None or null or alike.
Are there some tricks to do that?
The closest alternative I have found is to use a special sentinel value (the one you will probably use in Option::unwrap_or), which appears in the TOML file as the real value (e.g. 0), and converts from Option::None on serialization. But on deserialization, the sentinel value converts to Option::None and leaves us with the real Option type.
Serde has a special #[serde(with = module)] attribute to customize the ser/de field behavior, which you can use here. The full working example is here.
I'd like it so when the case is unknown, it will be associated with the last case
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(untagged)]
pub enum Action {
Action1,
Action2,
Action3,
Other(String), // when not known it should be here
}
I've tried using the directive
#[serde(untagged)]
but then it doesn't serialize properly
let b = Action::Action1;
let s = serde_json::to_string(&b);
let ss = s.unwrap();
println!("ss {:#?}", &ss);
let val = serde_json::to_value(b);
println!("ss {:#?}", &val);
results in
ss "null"
ss Ok(
Null,
)
Playground link
I can think of two options that build off each other.
First use From to turn this into a string every time you serialize and then From to turn it back into your own type. This requires you to convert every time you serialize and deserialize but will accomplish your goal.
If you want to make the API a little cleaner at the cost of doing more work you can implement serialize and deserialize yourself. Here are some references on how to do that:
Custom Serialization
Implementing Serialize
Implementing Deserialize
As a second option you can offload the custom serialization and deserialization if your willing to add another dependency of serde_with.
According to its docs:
De/Serializing a type using the Display and FromStr traits, e.g., for u8, url::Url, or mime::Mime. Check DisplayFromStr or serde_with::rust::display_fromstr for details.