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.
Related
I would like to expose in my REPL (built on top of Boa) the version of the js engine:
I'm trying to use a build.rs file for that:
use std::env;
fn main() {
println!("cargo:rustc-env=BOA_VERSION={}", "0.16.0");
}
And somewhere in my main.rs:
context.register_global_property("BOA_VERSION", env!("BOA_VERSION"), Attribute::all());
Obviously I have hardcoded the version of the crate but wondering if there is a programmatically way to get the version.
If all you need is to get access to the version information in your code, you can skip making a build.rs file for it. Instead, we can use the serde and toml crates to parse your Cargo.toml file directly. Below is a sample of how that might look.
Cargo.toml:
[package]
name = "dependency-information"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { version = "1.0.152", features = ["derive"] }
toml = "0.7.0"
main.rs:
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
enum DependencyValue {
String(String),
Object {
version: String,
features: Vec<String>,
},
}
#[derive(Debug, Serialize, Deserialize)]
struct CargoToml {
dependencies: HashMap<String, DependencyValue>,
}
fn main() {
let cargo_toml_raw = include_str!("../Cargo.toml");
let cargo_toml: CargoToml = toml::from_str(cargo_toml_raw).unwrap();
println!("{cargo_toml:#?}");
}
And if we run it, we get this output:
CargoToml {
dependencies: {
"toml": String(
"0.7.0",
),
"serde": Object {
version: "1.0.152",
features: [
"derive",
],
},
},
}
Note that you probably would want to include the Cargo.toml file dynamically instead of using include_str!. I used include_str! for the sake of the example.
I have a struct as follows?
pub struct Instrument {
pub id:i32,
pub expiry_on: <this should be a date field>
}
What should I give the type of expiry_on. I want to use the struct inside sqlx::query_as!() to fetch records from postgres ?
expiry_on is a timestampz Postgres field.
Under the assumption that your expiry_on field in postgres is a timestamptz: Depending on the time library you use, you will either want to add the chrono feature or the time feature to sqlx. Which you can do in Cargo.toml like this:
[dependencies]
sqlx = { version = "*", features = [ "chrono" ] }
or
sqlx = { version = "*", features = [ "time" ] }
instead of
sqlx = "*"
Where * is whatever version you're using.
You then change the following in Instrument (assuming chrono):
pub struct Instrument {
pub id: i32,
pub expiry_on: chrono::DateTime<chrono::Utc>,
}
How can I deserialize an XML-string (using serde derive) returned by an API that might have different children?
If I call an API it might return one of the following result sets:
use serde::Deserialize;
use serde_xml_rs::from_str;
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Error {
code: u32,
message: String,
}
#[derive(Debug, Deserialize)]
struct Item {
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct DataSet {
error: Option<Error>,
data: Option<Vec<Item>>
}
fn main() {
let error = r#"
<DataSet>
<Error>
<Code>0</Code>
<Message>error</Message>
</Error>
</DataSet>
"#;
let dataset: DataSet = from_str(error).unwrap();
println!("{:#?}", dataset);
let items = r#"
<DataSet>
<Item>..</Item>
<Item>..</Item>
</DataSet>
"#;
let dataset: DataSet = from_str(items).unwrap();
println!("{:#?}", dataset);
}
[package]
name = "dataset"
version = "0.1.0"
edition = "2018"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde-xml-rs = "0.3"
But I feel like there should be a more idiomatic way of achieving this. Especially since it would make matching off the data a lot easier.
I've tried using #[serde(flatten)], and I've tried setting ApiResponse to a tuple-struct and a struct with a single tuple parameter. No luck.
Is this possible without having to build a custom deserializer?
Take a look at some of the serde examples which might be able to help you here. Specifically the examples/docs on untagged enums which allow you to define many ways to deserialize into a single enum type.
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.
I need to implement the rustc_serialize::Decoder trait for my Herd struct:
extern crate chrono;
extern crate rustc_serialize;
use chrono::NaiveDate;
use rustc_serialize::Decodable;
struct Herd {
id: i32,
breed: String,
name: String,
purchase_date: NaiveDate,
}
impl Decodable for Herd {
fn decode<D: Decoder>(d: &mut D) -> Result<Herd, D::Error> {
d.read_struct("Herd", 4, |d| {
let id = try!(d.read_struct_field("id", 0, |d| d.read_i32()));
let breed = try!(d.read_struct_field("breed", 1, |d| d.read_str()));
let name = try!(d.read_struct_field("name", 2, |d| d.read_str()));
let purchase_date = try!(d.read_struct_field("purchase_date", 3, |i| {
i.read_struct("NaiveDate", 1, |i| {
let ymdf = try!(i.read_struct_field("ymdf", 0, |i| i.read_i32()));
Ok(NaiveDate { ymdf: ymdf })
})
}));
Ok(Herd {
id: id,
breed: breed,
name: name,
purchase_date: purchase_date,
})
})
}
}
I'm unable to use #[derive(RustcDecodable)] because the following error occurs:
error[E0277]: the trait bound chrono::NaiveDate: rustc_serialize::Decodable is not satisfied
I'm working on manually implementing Decodable and that's what you see in the code above. NaiveDate from the rust-chrono crate is a struct with one field that is of data type i32.
When I run the code right now, I receive the message:
field ymdf of struct chrono::NaiveDate is private
How do I implement Decoder for NaiveDate? Is there a simpler way to implement Decodable for my Herd struct? I am an all-around beginner programmer; perhaps there is another way to look at this problem.
I am using Rust 1.12 with the following dependencies:
nickel = "0.9.0"
postgres = { version = "0.12", features = ["with-chrono"]}
chrono = "0.2"
rustc-serialize = "0.3"
NaiveDate does implement Decodable but under an optional feature "rustc-serialize".
You should add this in your Cargo.toml to activate it:
chrono = { version = "0.2", features = ["rustc-serialize"]}
In order to be able to derive Decodable for Herd, NaiveDate would need to be Decodable too. Its ymdf field is private, though, which is an issue if you wanted to implement it manually.
What you could do is define your own wrapper type for ymdf (and use it in Herd) - then you should be able to derive Decodable for it and then for Herd.
Edit: It's not necessarily the way to go with NaiveDate, as there is no easy way to convert ymdf into it (or to import DateImpl, its type), but depending on your needs the following way might be enough:
use std::i32;
pub type DateImpl = i32;
pub const MAX_YEAR: DateImpl = i32::MAX >> 13;
pub const MIN_YEAR: DateImpl = i32::MIN >> 13;
#[derive(RustcDecodable)]
struct YmdfWrapper {
ymdf: DateImpl
}
#[derive(RustcDecodable)]
struct Herd {
id: i32,
breed: String,
name: String,
purchase_date: YmdfWrapper,
}